Spring Boot仅配置X509客户端认证启动失败:无信任材料配置问题咨询
Spring Boot反向代理下仅配置X509客户端认证的解决方案
你遇到的报错是因为Spring Security启用X509认证时,默认会要求配置SSL信任材料,但你的场景是反向代理已经处理了HTTPS,不需要应用自身配置服务端SSL(密钥库keystore),但仍需配置信任库(truststore)来验证客户端证书的合法性,同时要调整认证逻辑,基于反向代理转发的证书信息来做认证,而不是直接处理SSL连接。
下面是具体的解决步骤:
1. 关闭应用自身的SSL启用
因为反向代理已经接管了HTTPS,所以要在配置文件里关闭应用的SSL:
# application.properties server.ssl.enabled=false
确保反向代理正确转发客户端证书相关信息,比如将客户端证书的PEM字符串放在X-SSL-CERT这类请求头中,或者转发证书链的相关标识。
2. 配置Spring Security的X509认证逻辑
不需要依赖容器的SSL终止,而是自定义证书提取和认证逻辑:
基础X509配置(适用于反向代理已将证书注入请求的场景)
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .x509(x509 -> x509 // 从证书主题中提取用户名,这里匹配CN字段 .subjectPrincipalRegex("CN=(.*?)(?:,|$)") // 根据提取的用户名加载用户信息 .userDetailsService(customUserDetailsService()) ); return http.build(); } @Bean public UserDetailsService customUserDetailsService() { return username -> { // 这里替换成你的用户匹配逻辑,比如根据CN查询数据库 return User.withUsername(username) .password("{noop}unused") // 因为用证书认证,密码可设为占位符 .roles("USER") .build(); }; } }
自定义证书提取(从请求头获取证书)
如果反向代理是通过请求头传递证书PEM,需要自定义证书提取逻辑:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth .anyRequest().authenticated() ) .addFilterBefore(customX509Filter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } @Bean public X509AuthenticationFilter customX509Filter() { X509AuthenticationFilter filter = new X509AuthenticationFilter(); filter.setAuthenticationManager(authenticationManager()); filter.setX509PrincipalExtractor(request -> { // 从请求头获取证书PEM String certPem = request.getHeader("X-SSL-CERT"); if (certPem == null) { throw new BadCredentialsException("No client certificate found in request"); } // 解析PEM格式为X509Certificate try { String cleanedPem = certPem.replaceAll("-----BEGIN CERTIFICATE-----", "") .replaceAll("-----END CERTIFICATE-----", "") .replaceAll("\\s+", ""); byte[] certBytes = Base64.getDecoder().decode(cleanedPem); CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(certBytes)); // 返回证书主题作为用户名,可根据需求调整 return cert.getSubjectX500Principal().getName(); } catch (Exception e) { throw new BadCredentialsException("Failed to parse client certificate", e); } }); return filter; } @Bean public AuthenticationManager authenticationManager() { return new ProviderManager(Arrays.asList(x509AuthenticationProvider())); } @Bean public X509AuthenticationProvider x509AuthenticationProvider() { X509AuthenticationProvider provider = new X509AuthenticationProvider(); provider.setUserDetailsService(customUserDetailsService()); // 配置信任锚,验证证书是否由信任的CA签发 provider.setTrustAnchors(getTrustAnchors()); return provider; } private Set<TrustAnchor> getTrustAnchors() throws Exception { // 加载信任库,里面存放信任的CA证书 KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); try (InputStream is = getClass().getResourceAsStream("/truststore.jks")) { trustStore.load(is, "your-truststore-password".toCharArray()); } return TrustAnchorUtils.createTrustAnchors(trustStore); } @Bean public UserDetailsService customUserDetailsService() { return username -> { // 自定义用户加载逻辑 return User.withUsername(username) .password("{noop}unused") .roles("USER") .build(); }; } }
3. 配置信任库(truststore)
无论哪种方式,都需要配置信任库来验证客户端证书是否由你信任的CA签发。你需要将信任的CA证书导入到JKS格式的信任库中,然后在配置文件或代码中指定:
# application.properties server.ssl.trust-store=classpath:truststore.jks server.ssl.trust-store-password=your-truststore-password
总结
不是必须配置服务端SSL(keystore),但必须配置信任材料(truststore)来验证客户端证书的合法性。通过调整Spring Security的认证逻辑,让它基于反向代理转发的证书信息进行认证,就能实现只启用客户端X509认证而不配置服务端SSL的需求。
内容的提问来源于stack exchange,提问作者Werzi2001
相关产品推荐
相关产品推荐

