aslary
aslary

Reputation: 421

@PermitAll annotation is ignored in Spring Boot 3

I am currently working on a simple app which utilizes a monolithic architecture, in which I am using a basic form of role based authentication (using self-signed JWTs). The setup and issuing of the tokens works fine.

I am using Spring Security. By default, I want everything to be locked down, that is, if I want to specifically enable access to an endpoint method, I would like to do so only in the endpoint, using the @PermitAll annotation.

However, that annotation is ignored, so I have to specify the public endpoints manually in the Spring Config, specifically, in the "requestMatchers". I don't like this solution, as I have to write the endpoint paths twice (in the controller, and in the security config):

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    return http
            .csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(auth ->
                    auth
                            .requestMatchers("/register", "/login").permitAll()
                            .anyRequest().authenticated()
            )
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.decoder(jwtDecoder())))
            .exceptionHandling(ex ->
                    ex.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
                            .accessDeniedHandler(new BearerTokenAccessDeniedHandler()))
            .build();
}

Furthermore, the endpoint looks as follows:

@RestController
@RequiredArgsConstructor
public class AuthController {

    private final AuthService authService;

    @GetMapping
    public String foo() {
        return "Only authenticated users can use this";
    }

    // @PermitAll is ignored here, manual config is needed
    @PostMapping("login")
    public String login(@RequestBody LoginDto loginDto) {
        return authService.login(loginDto);
    }

    // @PermitAll is ignored here, manual config is needed
    @PostMapping("register")
    public String register(@RequestBody RegisterDto registerDto) {
        return authService.register(registerDto);
    }

}

The security config class is annotated with the following annotations:

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(securedEnabled = true, jsr250Enabled = true)

Note: Mind you, all other annotations (e.g. Secured/RolesAllowed, ...) are working as expected.

Upvotes: 0

Views: 532

Answers (1)

Angela Lopez
Angela Lopez

Reputation: 123

I think this has to do with the default behaviour of spring security's SecurityFilterChain. You haven't provided your non-working SecurityFilterChain configuration but I'll assume the authorizeHttpRequests lambda looked something like this:

authorizeHttpRequests(auth ->
                    auth.anyRequest().authenticated())

Since spring dispatches the call to the first FilterChain that matches and you probably defined your FilterChain as requiring authentication for every endpoints (if the assumption above holds), then regardless of what you annotated your endpoint with, access will be denied.

I don't think it's particularly bad practice to duplicate your public endpoint's paths but if you don't want to do that, you can use a common prefix to all your public endpoints and match that prefix with wildcards in your security configuration:

authorizeHttpRequests(auth ->
                     auth.requestMatchers("/your-prefix/**").permitAll()
                    .anyRequest().authenticated())

Upvotes: 1

Related Questions