Spring Boot手动处理JWT:自定义OpenIdConnectFilter未触发求助
我来帮你梳理下问题所在,你的过滤器没被调用、返回403,主要是配置上的几个细节没处理好,咱们一步步来修正:
1. 过滤器添加位置错误,导致未加入有效过滤器链
你现在用了addFilterAfter(OpenIdConnectFilter("/secure"), OpenIDAuthenticationFilter::class.java),但这里有个核心问题:如果OpenIDAuthenticationFilter本身没有被Spring Security加载到过滤器链中,addFilterAfter就找不到对应的前置过滤器,你的自定义过滤器自然不会被添加进去。
另外,你构造过滤器时指定的拦截路径是/secure,如果你的受保护请求不是指向这个路径(比如是/api/**或者其他业务路径),过滤器也不会触发。
修正方案:
把过滤器添加到UsernamePasswordAuthenticationFilter之前(这个过滤器是Spring Security默认存在的),同时根据你的实际需求设置拦截路径,比如拦截所有受保护请求可以用/**:
@Configuration @EnableWebSecurity class WebSecurityConfig : WebSecurityConfigurerAdapter() { @Throws(Exception::class) override fun configure(http: HttpSecurity) { http // 添加自定义过滤器到默认表单认证过滤器之前 .addFilterBefore(openIdConnectFilter(), UsernamePasswordAuthenticationFilter::class.java) .authorizeRequests() .antMatchers("/", "/home").permitAll() .anyRequest().authenticated() } // 给自定义过滤器注入AuthenticationManager @Bean fun openIdConnectFilter(): OpenIdConnectFilter { val filter = OpenIdConnectFilter("/**") filter.setAuthenticationManager(authenticationManagerBean()) return filter } }
2. 自定义过滤器缺少AuthenticationManager配置
AbstractAuthenticationProcessingFilter的子类必须设置AuthenticationManager才能正常处理认证流程,否则即使过滤器被触发,也无法完成认证逻辑。上面的配置里已经通过filter.setAuthenticationManager(authenticationManagerBean())解决了这个问题。
3. 安全配置中重复的authorizeRequests导致配置覆盖
你原来的代码里写了两次.authorizeRequests(),这会导致第一次的配置被第二次覆盖,虽然这里不影响过滤器触发,但会导致权限配置异常,建议合并成一个统一的权限规则块。
4. 确保过滤器正确处理JWT请求
最后检查你的OpenIdConnectFilter实现,要确保它能从请求中正确提取JWT令牌(通常是从Authorization头的Bearer前缀后获取),并在attemptAuthentication方法中完成验证:
class OpenIdConnectFilter(defaultFilterProcessesUrl: String) : AbstractAuthenticationProcessingFilter(defaultFilterProcessesUrl) { // 替换成你的实际JWT密钥 private val SECRET_KEY = "your-strong-secret-key-here" override fun attemptAuthentication(request: HttpServletRequest, response: HttpServletResponse): Authentication { // 从请求头获取JWT令牌 val authHeader = request.getHeader("Authorization") if (authHeader == null || !authHeader.startsWith("Bearer ")) { throw BadCredentialsException("Missing or invalid Authorization header") } val token = authHeader.substring(7) // 解析并验证JWT签名和内容 val claims = Jwts.parser() .setSigningKey(SECRET_KEY.toByteArray()) .parseClaimsJws(token) .body // 从JWT中提取用户信息和权限列表 val username = claims.subject val authorities = claims.get("authorities", List::class.java) .map { SimpleGrantedAuthority(it.toString()) } // 返回认证对象,后续Spring Security会自动处理会话 return UsernamePasswordAuthenticationToken(username, null, authorities) } override fun successfulAuthentication(request: HttpServletRequest, response: HttpServletResponse, chain: FilterChain, authResult: Authentication) { // 将认证结果存入SecurityContext,后续请求会自动识别已认证用户 SecurityContextHolder.getContext().authentication = authResult chain.doFilter(request, response) } }
做完这些修改后,你的过滤器应该就能正常拦截请求并进行JWT验证了,记得测试时确保请求头里携带正确的Bearer <JWT_TOKEN>格式令牌。
内容的提问来源于stack exchange,提问作者IEE1394

