
spring security 升级至 6.x 后,`securityfilterchain` 配置方式变更,但常见 401 错误往往并非源于配置本身,而是组件扫描遗漏导致自定义 filter(如 `jwtrequestfilter`)未被 spring 容器管理,从而未生效。本文详解典型陷阱及正确迁移实践。
在将 Spring Boot 应用从旧版本(如 2.7.x)升级至 Spring Boot 3.x + Spring Security 6.x 时,许多开发者会按官方迁移指南重构安全配置,改用 SecurityFilterChain Bean 替代已废弃的 WebSecurityConfigurerAdapter。你提供的新配置逻辑本身是正确的:
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.cors(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers(Endpoints.PUBLIC_ENDPOINTS).permitAll() // ✅ 正确声明公开端点
.anyRequest().authenticated()
)
.exceptionHandling(ex -> ex.authenticationEntryPoint(jwtAuthenticationEntryPoint))
.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); // ⚠️ 关键依赖在此
return http.build();
}这段代码语义清晰:禁用 CSRF/CORS、放行 PUBLIC_ENDPOINTS(如 /authentication/login)、其余请求需认证,并注册 JWT 过滤器。但它能正常工作的前提是:jwtRequestFilter 必须是一个由 Spring 管理的 Bean。
而问题根源恰恰在于——你的 JwtRequestFilter(或相关安全组件)未被 Spring 扫描到。你原来的 @SpringBootApplication(scanBasePackages = {...}) 显式限定了包扫描范围,却意外排除了 JwtRequestFilter 所在包(例如 de.company.app.filter 或 de.company.app.security.filter),导致该 Filter 虽被 addFilterBefore 引用,实则为 null 或未注入依赖(如 jwtTokenUtil),最终 JWT 解析失败,认证流程中断,返回 401 Unauthorized。
✅ 解决方案非常简洁:
// ❌ 错误:扫描范围过窄,遗漏 Filter 类
//@SpringBootApplication(scanBasePackages = {"de.company.app.data.user", "de.company.app.security"})
// ✅ 正确:使用默认扫描(推荐),或显式包含 Filter 所在包
@SpringBootApplication
// 或更精确地:
// @SpringBootApplication(scanBasePackages = {
// "de.company.app",
// "de.company.app.filter", // ← 确保包含 JwtRequestFilter 所在包
// "de.company.app.security"
// })? 验证与最佳实践建议:
- 在 JwtRequestFilter 类上添加 @Component(若非 @Bean 方式声明),并确认其类路径在扫描范围内;
- 启动时检查日志,搜索 JwtRequestFilter 是否被 Spring 注册(如 Creating shared instance of singleton bean 'jwtRequestFilter');
- 使用 @ConditionalOnBean(JwtRequestFilter.class) 在配置类中做防御性校验;
- 避免在 SecurityFilterChain 中直接 new JwtRequestFilter(...) —— 这将绕过 Spring 生命周期管理,导致依赖注入失败。
总结:Spring Security 6 的配置迁移本身是直观且健壮的,但上下文初始化完整性(尤其是组件扫描)比配置语法更易引发隐蔽故障。遇到 permitAll() 端点仍返回 401,请优先排查自定义 Filter 是否真正成为 Spring Bean,而非反复调整 requestMatchers 表达式。










