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

3

10

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 ?

Telltale answered 13/11, 2015 at 22:35 Comment(0)
L
0

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://mcmap.net/q/1098426/-multi-user-restful-api-using-spring-boot-jpa-and-security

Litha answered 14/11, 2015 at 19:19 Comment(0)
C
0

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);
}
Cassidycassie answered 29/4, 2022 at 22:5 Comment(0)
C
-1

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.
Counterpoise answered 14/11, 2015 at 7:19 Comment(3)
The question was how to restrict the results to only the entities that belong to the user. Roles do not help in any way. I don't care if a user is an administrator or a mortal user - he should not be allowed to see entities that belong to himself.Upsilon
huh?its always good to attach roles to your item list or resources.Its a good practice.If not required,then you could just iterate based on a key from JPA repository via : repository.findByUserId(Long userId),which is just plain spring data jpa,(no spring security as such,conflicts with the question).And moreover,in above statement you are contradicting yourself "he should not be allowed to see entities that belong to himself" .. please correct it..thanks.Counterpoise
Yeah should have been "that do not belong to himself" but it looks like SO does not allow editing of comments after a while. Yeah, roles are fine and I do use them. But this has nothing to do with the situation in the question. The problem is that spring-data-rest makes all repositories available under the mapping /{repository} allowing access to all entities. So the question is how to return only entities that belong to the user.Upsilon

© 2022 - 2024 — McMap. All rights reserved.