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

升级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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 10:29:49