Spring Security 6(OAuth2ResourceServer)调用私有REST请求遇PKIX路径构建失败
解决方案:Spring Boot 3 + Opaque Token资源服务器PKIX证书错误处理
问题核心
PKIX路径构建失败是因为资源服务器调用授权服务器的Token introspection端点(HTTPS)时,JVM不信任对方的SSL证书。Spring Security 6中默认的SpringOpaqueTokenIntrospector使用未配置自定义信任库的RestTemplate,因此即使证书已导入密钥库,也可能未被正确加载使用。
步骤1:确认证书导入有效性
先验证授权服务器的证书是否已正确导入JVM信任库或自定义信任库:
- 查看默认JVM信任库中的证书:
默认密码为keytool -list -keystore $JAVA_HOME/lib/security/cacerts -alias <你的证书别名>changeit,若输出包含目标证书,说明导入成功;若使用自定义信任库,替换路径为你的信任库路径。
步骤2:自定义带SSL信任配置的Opaque Token Introspector
通过自定义RestTemplate并注入到SpringOpaqueTokenIntrospector,确保调用introspection端点时使用指定的信任库:
2.1 构建SSL上下文
创建加载自定义信任库的SSL上下文Bean:
@Bean public SSLContext sslContext(@Value("${truststore.path}") String truststorePath, @Value("${truststore.password}") String truststorePassword) throws Exception { KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType()); try (InputStream is = new FileInputStream(truststorePath)) { trustStore.load(is, truststorePassword.toCharArray()); } TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(trustStore); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom()); return sslContext; }
2.2 配置带SSL的RestTemplate
基于上述SSL上下文构建专用的RestTemplate:
@Bean public RestTemplate opaqueTokenRestTemplate(SSLContext sslContext) { HttpClient httpClient = HttpClientBuilder.create() .setSSLContext(sslContext) .build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); return new RestTemplate(requestFactory); }
2.3 自定义OpaqueTokenIntrospector
将带SSL的RestTemplate注入到SpringOpaqueTokenIntrospector:
@Bean public OpaqueTokenIntrospector opaqueTokenIntrospector(RestTemplate opaqueTokenRestTemplate, @Value("${auth.server.url}") String introspectionUri, @Value("${auth.server.clientId}") String clientId, @Value("${auth.server.clientsecret}") String clientSecret) { SpringOpaqueTokenIntrospector introspector = new SpringOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret); introspector.setRestOperations(opaqueTokenRestTemplate); return introspector; }
步骤3:修复SecurityFilterChain的重复配置问题
代码中重复配置oauth2ResourceServer会导致配置覆盖,合并为一次配置并使用自定义的Introspector:
@Bean public SecurityFilterChain configure(HttpSecurity http, OpaqueTokenIntrospector opaqueTokenIntrospector) throws Exception { http.addFilterAfter(new OncePerRequestFilter() { @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { var authHeader = request.getHeader(HEADER_NAME); if (StringUtils.isEmpty(authHeader) || !StringUtils.startsWith(authHeader, BEARER_PREFIX)) { SecurityContextHolder.clearContext(); } filterChain.doFilter(request, response); } }, AbstractPreAuthenticatedProcessingFilter.class); // 统一配置授权规则 http.authorizeHttpRequests(auth -> auth .requestMatchers("/", "/v1/open/**").permitAll() .requestMatchers("/pension/**").authenticated() .requestMatchers((request) -> StringUtils.equalsIgnoreCase(activeProfile, "dev"), "/webjars/**", "/resources/**", "/swagger-ui.html", "/swagger-resources/**", "/v2/api-docs", "index.html").permitAll() .anyRequest().authenticated()); // 配置资源服务器使用自定义Introspector http.oauth2ResourceServer(oauth2 -> oauth2.opaqueToken(t -> t.introspector(opaqueTokenIntrospector))); return http.build(); }
额外排查点
- 端点URL正确性:检查
auth.server.url是否为授权服务器的Token introspection完整端点(通常类似https://xxx/oauth2/introspect),错误信息中的URLhttps://blabla/dev/agent-auth/oauth/可能路径不完整,导致请求地址错误。 - JVM启动参数:若使用默认JVM信任库仍未生效,可通过启动参数强制指定信任库:
-Djavax.net.ssl.trustStore=/path/to/your/truststore.jks -Djavax.net.ssl.trustStorePassword=your-password - 证书类型:确保导入的是授权服务器的根CA证书,而非服务器证书本身,避免证书链不完整。
内容的提问来源于stack exchange,提问作者Muratbek Bauyrzhan
相关产品推荐
相关产品推荐

