Spring Security 无法同时支持需认证与免认证端点的问题求助
问题根因
- 安全配置写法错误:
configure(HttpSecurity http)方法中重复调用了两次authorizeRequests(),且自定义的JWT权限转换器没有加@Bean注解,也没有注册到OAuth2资源服务器配置中,导致JWT校验逻辑异常,公开路径的permitAll规则被后续的全局认证规则覆盖,访问公开接口返回401。 - 认证拦截逻辑失效:调整配置时错误放开了全局匿名访问权限,导致原本需要认证的端点没有被Spring Security拦截,未携带JWT的请求直接进入Controller层,
Authentication对象为空触发空指针,抛出500错误。 - 规则匹配顺序错误:Spring Security的路径匹配规则遵循从上到下优先匹配原则,错误的配置顺序导致公开路径规则未生效。
解决方案
第一步:修正SecurityConfig配置
核心修改点:
- 合并两次
authorizeRequests()配置,将公开路径规则放在最前面 - 给
jwtAuthenticationConverter()方法添加@Bean注解,并注册到JWT配置中 - 保留全局认证规则,确保非公开路径必须经过认证才能访问
@Configuration @EnableWebSecurity(debug = false) @EnableGlobalMethodSecurity( securedEnabled = true, jsr250Enabled = true, prePostEnabled = true ) public class SecurityConfig extends WebSecurityConfigurerAdapter { @Value("${auth0.audience}") private String audience; @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}") private String issuer; @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("http://localhost:3010")); configuration.setAllowedMethods(Arrays.asList("GET", "POST")); configuration.setAllowCredentials(true); configuration.addAllowedHeader("Authorization"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } @Override protected void configure(HttpSecurity http) throws Exception { http .cors() .and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .headers().referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.SAME_ORIGIN) .and() .xssProtection() .and() .contentSecurityPolicy("script-src 'self'").and() .and() .csrf() .disable() .formLogin() .disable() .httpBasic() .disable() .exceptionHandling() .authenticationEntryPoint(new RestAuthenticationEntryPoint()) .and() // 路径匹配规则按顺序生效,公开路径放前面 .authorizeRequests() .antMatchers("/api/v1/admin/**").permitAll() .anyRequest().authenticated() .and() .oauth2ResourceServer() .jwt() // 注册自定义的JWT权限转换器 .jwtAuthenticationConverter(jwtAuthenticationConverter()); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/health", "/health/**"); } @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注解,让Spring托管该转换器 @Bean JwtAuthenticationConverter jwtAuthenticationConverter() { JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter(); converter.setAuthoritiesClaimName("permissions"); converter.setAuthorityPrefix(""); JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter(); jwtConverter.setJwtGrantedAuthoritiesConverter(converter); return jwtConverter; } }
第二步:验证效果
- 公开路径
/api/v1/admin/**无需携带JWT即可正常访问 - 其他需要认证的路径未携带JWT时,会直接被Spring Security拦截,触发你实现的
RestAuthenticationEntryPoint返回401错误,不会进入Controller层,不会出现空指针500问题 - 携带合法JWT访问认证路径时,
Authentication对象会正常注入,原有业务逻辑无需修改
内容的提问来源于stack exchange,提问作者MetaCoder
相关产品推荐
相关产品推荐

