Spring Security 6配置requestMatchers时如何包含带查询参数的端点?
Let's break down your two issues and fix them step by step:
Issue 1: Getting 403 instead of 401 when accessing /person without a token
When an unauthenticated user hits an endpoint that requires specific authorities, Spring Security defaults to returning a 403 (Forbidden) response instead of 401 (Unauthorized). This happens because the framework checks for required permissions first, and since anonymous users have no authorities, it returns 403 immediately rather than prompting authentication.
Issue 2: /person?email=me@me.co bypasses permission checks and returns 200
The root cause here is that your security configuration doesn't enable OAuth2 resource server support. You defined a JwtDecoder bean, but never told Spring Security to use it for validating JWT tokens. Without this setup, Spring Security treats all requests as anonymous, which explains the inconsistent behavior:
- For
/person(no query params), thehasAuthority("SCOPE_blahablah")rule kicks in, and since anonymous users lack this authority, you get 403. - For
/person?email=me@me.co, it’s likely either matching a different rule (likeanyRequest().authenticated()which doesn’t enforce anything without OAuth2 setup) or your controller method has an annotation like@PermitAllthat skips security checks.
Full Fixed SecurityConfig Code
Here's the updated configuration that addresses both issues:
import com.purepm.admin.services.authentication.AudienceValidator; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator; import org.springframework.security.oauth2.core.OAuth2TokenValidator; import org.springframework.security.oauth2.jwt.*; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.oauth2.server.resource.authentication.OAuth2AuthenticationEntryPoint; @EnableWebSecurity @Configuration public class SecurityConfig{ @Value("${auth0.audience}") private String audience; @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}") private String issuer; @Bean JwtDecoder jwtDecoder() { NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder) JwtDecoders.fromOidcIssuerLocation(issuer); OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience); OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer); OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator); jwtDecoder.setJwtValidator(withAudience); return jwtDecoder; } @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .requestMatchers("/").permitAll() // Matches /person requests regardless of query parameters .requestMatchers("/person").hasAuthority("SCOPE_blahablah") .anyRequest().authenticated() ) // Enable OAuth2 resource server with your custom JwtDecoder .oauth2ResourceServer(oauth2 -> oauth2.jwt(jwt -> jwt.decoder(jwtDecoder()))) // Configure unauthenticated requests to return 401 instead of 403 .exceptionHandling(exceptions -> exceptions .authenticationEntryPoint(new OAuth2AuthenticationEntryPoint()) ); return http.build(); } }
Additional Checks to Ensure Everything Works
- Verify your AudienceValidator: Make sure it correctly validates that the token's
audclaim includes your configured${auth0.audience}value. A broken validator could allow invalid tokens to pass. - Check your controller: Ensure the
/personendpoint method doesn't have annotations like@PermitAllor@AnonymousAllowedthat bypass security. - Test with different tokens:
- No token: Should return 401
- Valid token without
SCOPE_blahablah: Should return 403 - Valid token with
SCOPE_blahablah: Should return 200 (with or without query parameters)
内容的提问来源于stack exchange,提问作者Jose Gleeson

