You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security 6 未认证端点配置失效,请求返回401未授权

Spring Boot 3.0.1 + Spring Security 6.0 公共端点返回401问题解决

问题场景

升级Spring Boot至3.0.1、Spring Security至6.0版本后,配置为无需认证的公共端点(如/rest/public/export/)调用时始终返回401未认证。已按照官方指南移除WebSecurityConfigurerAdapter,当前Security配置如下:

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
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 httpSecurity) throws Exception {
        return httpSecurity
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/rest/public/**").permitAll()
                        .anyRequest().authenticated()
                )
                .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
                .build();
    }
}

调用日志显示请求被标记为匿名,但仍被拦截:

o.s.security.web.FilterChainProxy        : Securing GET /rest/public/export/
o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
o.s.s.w.s.HttpSessionRequestCache        : Saved request http://localhost:8090/rest/public/export/?continue to session

问题原因

  1. 无状态模式下请求缓存冲突:配置了SessionCreationPolicy.STATELESS但未禁用默认的HttpSessionRequestCache,该缓存尝试将请求保存到session,但无状态模式下session不存在,导致认证流程异常。
  2. 方法级安全注解干扰:若公共端点的控制器方法添加了@PreAuthorize等方法级安全注解,即使HttpSecurity配置了permitAll,方法级校验仍会要求认证。
  3. 路径匹配细节问题:Spring Security 6的路径匹配默认对尾斜杠敏感,若请求路径带尾斜杠而匹配规则未覆盖,可能导致匹配失败。

解决方案

1. 调整SecurityFilterChain配置

禁用请求缓存并配置异常处理,确保无状态模式下正常返回401而非触发无效的session操作:

@Bean
public SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
    return httpSecurity
            .csrf(csrf -> csrf.disable())
            .authorizeHttpRequests(auth -> auth
                    // 确保路径匹配覆盖带/不带尾斜杠的情况
                    .requestMatchers("/rest/public/**", "/rest/public/**/").permitAll()
                    .anyRequest().authenticated()
            )
            .sessionManagement(sess -> sess.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
            // 禁用请求缓存,适配无状态模式
            .requestCache(requestCache -> requestCache.nullRequestCache())
            // 配置异常处理,直接返回401错误
            .exceptionHandling(exceptions -> exceptions
                    .authenticationEntryPoint((request, response, authException) -> {
                        response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
                    })
            )
            .build();
}

2. 检查控制器方法注解

移除公共端点控制器方法上的@PreAuthorize、@Secured等强制认证的注解,确保HttpSecurity的permitAll生效。

3. 验证路径匹配规则

若仍存在匹配问题,可显式配置路径匹配策略为非严格模式:

@Bean
public PathMatcher pathMatcher() {
    AntPathMatcher matcher = new AntPathMatcher();
    matcher.setTrimTokens(false);
    matcher.setCaseSensitive(false);
    return matcher;
}

验证

修改配置后重启服务,再次调用公共端点,应正常返回业务响应而非401。

内容的提问来源于stack exchange,提问作者Stuzfuz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 20:15:41