spring-security icon indicating copy to clipboard operation
spring-security copied to clipboard

Different behaviour of `@RolesAllowed` annotation on `@EnableMethodSecurity` vs `@EnableGlobalMethodSecurity`

Open mgr32 opened this issue 2 years ago • 0 comments

Describe the bug

Consider the following controller:

@RestController
public class SecuredController {

    @GetMapping(path = "/rolesAllowed_GUEST")
    @RolesAllowed("GUEST")
    public String rolesAllowed_GUEST() {
        return "GUEST";
    }

    @GetMapping(path = "/rolesAllowed_ROLE_GUEST")
    @RolesAllowed("ROLE_GUEST")
    public String rolesAllowed_ROLE_GUEST() {
        return "ROLE_GUEST";
    }

}
  1. When legacy @EnableGlobalMethodSecurity(jsr250Enabled = true) is used, both endpoints are accessible when request is authenticated with ROLE_GUEST:

    • this is because ROLE_ prefix is added only when not already present in @RolesAllowed value (code).
  2. After switching to the new annotation (@EnableMethodSecurity(jsr250Enabled = true), /rolesAllowed_GUEST works ok, but /rolesAllowed_ROLE_GUEST returns 403 instead of 200:

To Reproduce

  1. Enable method security with @EnableMethodSecurity(jsr250Enabled = true).
  2. Create a REST Controller with @RolesAllowed("ROLE_GUEST").
  3. Configure a simple UserDetailsService:
    @Bean
        public UserDetailsService userDetailsService() {
            return new InMemoryUserDetailsManager(User.builder().username("guest").password("{noop}guest").roles("GUEST").build());
        }
    
  4. Start the server.
  5. Send a request with basic auth:
    curl --request GET 'http://localhost:8080/rolesAllowed_ROLE_GUEST' --header 'Authorization: Basic Z3Vlc3Q6Z3Vlc3Q='
    
  6. Observe the response code - it is 403 instead of 200.
  7. Stop the server, switch the @EnableMethodSecurity annotation to @EnableGlobalMethodSecurity and retry the test. Observe the response code - it is 200.

Tested on Spring Security 5.7.2, but the code that unconditionally adds the prefix seems to be the same on main. See the sample repository provided below for a complete example.

Expected behavior

@EnableMethodSecurity should behave in the same way as @EnableGlobalMethodSecurity regarding conditional adding of ROLE_ prefix. Alternatively, the change should be documented in Method Security docs, as it can be breaking for some applications.

Sample

https://github.com/mgr32/spring-security-method-security-issue

Summary including other annotations (assuming HTTP request with proper Authorization header):

Enabling annotation Endpoint annotation HTTP response status code
@EnableGlobalMethodSecurity @RolesAllowed("GUEST") :heavy_check_mark: 200
@EnableMethodSecurity @RolesAllowed("GUEST") :heavy_check_mark: 200
@EnableGlobalMethodSecurity @RolesAllowed("ROLE_GUEST") :heavy_check_mark: 200
@EnableMethodSecurity @RolesAllowed("ROLE_GUEST") :x: :exclamation: 403
@EnableGlobalMethodSecurity @Secured("GUEST") :x: 403
@EnableMethodSecurity @Secured("GUEST") :x: 403
@EnableGlobalMethodSecurity @Secured("ROLE_GUEST") :heavy_check_mark: 200
@EnableMethodSecurity @Secured("ROLE_GUEST") :heavy_check_mark: 200
@EnableGlobalMethodSecurity @PreAuthorize("hasRole('GUEST')") :heavy_check_mark: 200
@EnableMethodSecurity @PreAuthorize("hasRole('GUEST')") :heavy_check_mark: 200
@EnableGlobalMethodSecurity @PreAuthorize("hasRole('ROLE_GUEST')") :heavy_check_mark: 200
@EnableMethodSecurity @PreAuthorize("hasRole('ROLE_GUEST')") :heavy_check_mark: 200

mgr32 avatar Aug 12 '22 11:08 mgr32