升级Spring Security 6后单元测试报错:Access Denied及证书缺失
问题分析与修复方案
核心原因
Spring Security 6.0 对PreAuthenticatedAuthenticationToken的处理逻辑做了调整:
- 旧版本中部分自动配置逻辑被移除,
PreAuthenticatedAuthenticationProvider不再自动关联证书信息的传递流程 - 若未显式配置
AuthenticationUserDetailsService或正确构建认证令牌,会导致certificate字段无法被正确注入,最终触发权限校验失败
修复步骤
1. 修正PreAuthenticatedAuthenticationToken构建逻辑
在JWT解析过滤器中,确保构建令牌时显式传入证书信息。新版本中PreAuthenticatedAuthenticationToken的构造参数要求更严格,需明确传递证书:
// 替换原令牌构建代码 X509Certificate certificate = // 从JWT中解析出证书实例 PreAuthenticatedAuthenticationToken authToken = new PreAuthenticatedAuthenticationToken(certificate, null); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
2. 显式配置PreAuthenticatedAuthenticationProvider
在Security配置类中,手动注册提供者并关联AuthenticationUserDetailsService,确保能根据证书加载用户权限:
@Bean public PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider() { PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider(); provider.setPreAuthenticatedUserDetailsService(certificateUserDetailsService()); return provider; } @Bean public AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> certificateUserDetailsService() { return token -> { X509Certificate cert = (X509Certificate) token.getPrincipal(); // 根据证书信息加载用户权限的自定义逻辑 UserDetails user = loadUserByCertificate(cert); return user; }; }
3. 更新SecurityFilterChain配置
确保PreAuthenticatedAuthenticationProvider被加入认证管理器,同时检查过滤器顺序,保证JWT解析过滤器在PreAuth相关逻辑之前执行:
@Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .antMatchers("/api/**").authenticated() ) .authenticationManager(authenticationManager(http.getSharedObject(AuthenticationConfiguration.class))) .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } private AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { ProviderManager manager = (ProviderManager) config.getAuthenticationManager(); manager.getProviders().add(preAuthenticatedAuthenticationProvider()); return manager; }
4. 调整测试配置
在测试类中,确保测试用JWT包含有效证书信息,并且测试配置的过滤器链与生产配置一致:
@Test void jwtAuthenticationTest() throws Exception { // 生成包含有效证书的JWT Token String token = generateJwtWithCertificate(validCertificate); mockMvc.perform(get("/api/protected") .header("Authorization", "Bearer " + token)) .andExpect(status().isOk()); }
关键验证点
- 调试时确认
PreAuthenticatedAuthenticationProvider.authenticate()方法接收的Authentication对象中principal为X509Certificate实例而非null - 检查
UserDetails加载逻辑是否正确从证书中提取用户权限,确保权限与接口要求匹配
内容的提问来源于stack exchange,提问作者Ivajlo Iliev
相关产品推荐
相关产品推荐

