'How do you secure SpringBoot / Spring-Data Rest so user can only access his own entities

I'm using Spring-Data/Rest (http://docs.spring.io/spring-data/rest/docs/current/reference/html/) with Spring Boot and basic Spring Security.

I have the following entities.

Items
  -->ID

User
 --> ID
 --> List<Items> items

Currently with spring rest, any user can see /items/1,2,3,4,5

I want only to allow users, to only see their own items.

Is this achievable without having to code a custom controller ?



Solution 1:[1]

I think multitenancy on JPA level could be a nice transparent approach to spearating the data a user can see. Please see my answer here for details: https://stackoverflow.com/a/33648305/5371736

Solution 2:[2]

Use Spring Security and Spring Security Data:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-data</artifactId>
</dependency>

Enable JPA Auditing:

@SpringBootApplication
@EnableJpaAuditing
public class SdrRlsApplication {
    public static void main(String[] args) {
        SpringApplication.run(SdrRlsApplication.class, args);
    }
}

Add an "owner" field (or what have you) to your entities:

@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class Foo {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String owner;

    private String name;

}

Add Spring Security SPeL expressions to your Repositories:

public interface FooRepository extends PagingAndSortingRepository<Foo, Long> {
    
    @Query(
        value="select f from Foo f where f.owner=?#{principal.username}",
        countQuery="select count(f) from Foo f where f.owner=?#{principal.username}"
    )
    Page<Foo> findAll(Pageable pageable);
}

Solution 3:[3]

Yes,you could.

For this what you could do is assign each User a particular role.For example ,in your case,assign user who owns the items as role column ADMIN and all others ANONYMOUS or USER,you pick.After this, using spring security you could make the request fail for the users having ANONYMOUS or USER role for the items URL and only allow users with ADMIN role to view the items.

Now,this could be achieved via spring security in multiple ways :

1.Using @PreAuthorize tags for individual controller methods and testing roles ADMIN/USER/.. But,i guess ,you do not want to modify the controller as such drastically.

  1. The short manual way,which is,to create authentication object into context holder and use spring boot security config,such as below,for example :

     @Order(1)
     public class UserFilter extends Filter {
    
     @Autowired
     UserService userService;
     ...
    
     UserObject userObject = userService.getUser(arg..);
     List<GrantedAuthority> grantedAuthorityList = new ArrayList<GrantedAuthority>();
     grantedAuthorityList.add( new SimpleGrantedAuthority((userObject.getRoleName()));//Either ROLE_ADMIN or ROLE_USER
    Authentication authentication = new    PreAuthenticatedAuthenticationToken(userObject.getId(), new Object(), grantedAuthorityList);
    SecurityContextHolder.getContext().setAuthentication(authentication);
    chain.doFilter(request,response);
    
    ...
    
    }
    

And the security configuration class :

  @Configuration
  @EnableWebSecurity
  public class SecurityConfigREST extends WebSecurityConfigurerAdapter {


  SecurityConfigREST(){
    super(true);
  }
  @Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    PreAuthenticatedAuthenticationProvider pap=new PreAuthenticatedAuthenticationProvider();
    pap.setPreAuthenticatedUserDetailsService(new PreAuthenticatedGrantedAuthoritiesUserDetailsService());
    auth.authenticationProvider(pap);
}
@Override
  protected void configure(HttpSecurity http) throws Exception {
   http
      .authorizeRequests()
      .regexMatchers("^/items.*$").hasAuthority("ROLE_ADMIN") //The role which should have access to /items/1,2.. URL
      .anyRequest().authenticated();
  }
  } 
  1. Use UserDetailsService in the security config above and load the user and its role in a preauthenticated authentication provider. Refer : http://docs.spring.io/autorepo/docs/spring-security/3.2.2.RELEASE/apidocs/org/springframework/security/core/userdetails/UserDetailsService.html

Having said all that ,its also a good design not to pass items (1,2,3) numbers via URL,as could lead to potential issues later,so use GET and pass JSON request body to it such as :

/items RequestMethod.GET 

{
"itemList" : [1,2,3,4,5]
}

Hope that helps.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Community
Solution 2
Solution 3