Spring Boot集成Pac4J与Keycloak时自签名证书加密异常
问题场景
运行Spring Boot 3.2.1并集成嵌入式Keycloak服务器,使用以下Maven依赖:
<dependency> <groupId>org.pac4j</groupId> <artifactId>jakartaee-pac4j</artifactId> <version>8.0.0</version> </dependency> <dependency> <groupId>org.pac4j</groupId> <artifactId>pac4j-oidc</artifactId> <version>6.0.1</version> </dependency>
无SSL配置时认证流程正常,启用SSL Bundle后:
- Web应用可正常通过HTTPS访问
- 本地浏览器能正常访问
https://localhost:8888/auth/realms/master/.well-known/openid-configuration
但Pac4J在认证流程中抛出异常:
org.pac4j.core.exception.TechnicalException: Error getting URL resource at org.pac4j.core.resource.SpringResourceHelper.getResourceInputStream(SpringResourceHelper.java:74) at org.pac4j.oidc.metadata.OidcOpMetadataResolver.retrieveMetadata(OidcOpMetadataResolver.java:89) at org.pac4j.oidc.metadata.OidcOpMetadataResolver.internalLoad(OidcOpMetadataResolver.java:76) at org.pac4j.core.resource.SpringResourceLoader.load(SpringResourceLoader.java:50) at org.pac4j.oidc.config.OidcConfiguration.findPkceMethod(OidcConfiguration.java:285) at org.pac4j.oidc.redirect.OidcRedirectionActionBuilder.addStateAndNonceParameters(OidcRedirectionActionBuilder.java:115) at org.pac4j.oidc.redirect.OidcRedirectionActionBuilder.getRedirectionAction(OidcRedirectionActionBuilder.java:58) at org.pac4j.core.client.IndirectClient.getRedirectionAction(IndirectClient.java:136) at org.pac4j.core.engine.DefaultSecurityLogic.redirectToIdentityProvider(DefaultSecurityLogic.java:240) at org.pac4j.core.engine.DefaultSecurityLogic.perform(DefaultSecurityLogic.java:160) at com.ebremer.server.MyCustomSecurityFilter.internalFilter(MyCustomSecurityFilter.java:79)
已配置trustAllCertificates和TrustAllHostnames,问题仍未解决。
解决方案
1. 为Pac4J显式配置信任所有证书的HTTP客户端
Pac4J默认HTTP客户端可能未复用你配置的SSL上下文,需手动绑定自定义客户端:
OidcConfiguration oidcConfig = new OidcConfiguration(); // 填充其他必要配置(clientId、clientSecret等) // 创建信任所有证书的HTTP客户端 org.pac4j.http.client.HttpClient pac4jHttpClient = new org.pac4j.http.client.HttpClient(); pac4jHttpClient.setSslContext(createTrustAllSslContext()); pac4jHttpClient.setHostnameVerifier((hostname, session) -> true); // 绑定到OIDC配置 oidcConfig.setHttpClient(pac4jHttpClient);
其中createTrustAllSslContext()实现:
private SSLContext createTrustAllSslContext() throws NoSuchAlgorithmException, KeyManagementException { SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, new TrustManager[]{new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) {} @Override public void checkServerTrusted(X509Certificate[] chain, String authType) {} @Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; } }}, new SecureRandom()); return sslContext; }
2. 自定义SpringResourceHelper绕过默认SSL验证
异常来自SpringResourceHelper的资源加载逻辑,可自定义实现替换默认类:
public class CustomSpringResourceHelper extends SpringResourceHelper { @Override public InputStream getResourceInputStream(String url) throws IOException { // 使用信任所有证书的HttpClient获取元数据 CloseableHttpClient client = HttpClientBuilder.create() .setSSLContext(createTrustAllSslContext()) .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE) .build(); HttpGet request = new HttpGet(url); HttpResponse response = client.execute(request); return response.getEntity().getContent(); } private SSLContext createTrustAllSslContext() throws NoSuchAlgorithmException, KeyManagementException { // 同上述SSLContext实现 } }
然后在OIDC配置中指定使用该助手:
OidcOpMetadataResolver metadataResolver = new OidcOpMetadataResolver(); metadataResolver.setResourceHelper(new CustomSpringResourceHelper()); oidcConfig.setOpMetadataResolver(metadataResolver);
3. 手动指定OIDC元数据跳过自动获取
如果自动加载元数据的问题无法解决,可直接手动注入元数据内容:
OidcOpMetadata metadata = new OidcOpMetadata(); metadata.setAuthorizationEndpoint("https://localhost:8888/auth/realms/master/protocol/openid-connect/auth"); metadata.setTokenEndpoint("https://localhost:8888/auth/realms/master/protocol/openid-connect/token"); metadata.setUserInfoEndpoint("https://localhost:8888/auth/realms/master/protocol/openid-connect/userinfo"); metadata.setJwksUri("https://localhost:8888/auth/realms/master/protocol/openid-connect/certs"); // 按需补充其他元数据字段 oidcConfig.setOpMetadata(metadata);
此方法会跳过Pac4J对元数据URL的请求,直接使用配置的端点信息。
4. 复用Spring Boot SSL Bundle的上下文
尝试将Spring Boot SSL Bundle生成的上下文注入Pac4J组件:
@Autowired private SSLBundle sslBundle; // 在OIDC客户端配置中使用 SSLContext sslContext = sslBundle.createSslContext(); org.pac4j.http.client.HttpClient pac4jHttpClient = new org.pac4j.http.client.HttpClient(); pac4jHttpClient.setSslContext(sslContext); pac4jHttpClient.setHostnameVerifier(sslBundle.getOptions().getHostnameVerifier()); oidcConfig.setHttpClient(pac4jHttpClient);
内容的提问来源于stack exchange,提问作者Erich Bremer
相关产品推荐
相关产品推荐

