Spring OAuth2资源服务器禁用SSL验证后仍出现SSL握手错误如何解决
问题原因
你当前的SSL禁用代码仅关闭了证书链信任校验,没有关闭HTTPS的主机名校验:HTTPS校验分为两个环节,第一步校验证书是否被信任,第二步校验访问的域名/IP和证书中配置的SAN(主体可选名称)或CN(通用名)匹配。你禁用第一步后解决了PKIX路径错误,但触发了第二步的主机名不匹配报错。
完整解决方案(仅适用于开发测试环境,生产环境严禁使用)
1. 修改SSL工具类,补充主机名校验禁用
import javax.net.ssl.*; import java.security.cert.X509Certificate; public final class SSLUtil { private static final TrustManager[] UNQUESTIONING_TRUST_MANAGER = new TrustManager[]{ new X509TrustManager() { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { } public void checkServerTrusted(X509Certificate[] certs, String authType) { } } }; // 新增全放行主机名验证器 private static final HostnameVerifier ALLOW_ALL_HOSTNAME_VERIFIER = (hostname, session) -> true; public static void turnOffSslChecking() throws Exception { final SSLContext sc = SSLContext.getInstance("SSL"); sc.init(null, UNQUESTIONING_TRUST_MANAGER, null); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); // 配置默认主机名验证器 HttpsURLConnection.setDefaultHostnameVerifier(ALLOW_ALL_HOSTNAME_VERIFIER); } public static void turnOnSslChecking() throws Exception { SSLContext.getInstance("SSL").init(null, null, null); HttpsURLConnection.setDefaultHostnameVerifier(HttpsURLConnection.getDefaultHostnameVerifier()); } private SSLUtil() { throw new UnsupportedOperationException("Do not instantiate libraries."); } }
2. 自定义JwtDecoder,使用配置了无SSL校验的RestTemplate拉取OIDC配置
Spring Security 默认的JwtDecoders.fromIssuerLocation内部使用的RestTemplate可能不会继承你配置的JDK全局SSL设置,更稳妥的方式是手动构造RestTemplate并传入配置:
import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtDecoders; import org.springframework.web.client.RestTemplate; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import java.net.HttpURLConnection; @Configuration public class JwtConfig { @Bean JwtDecoder jwtDecoderByIssuerUri(final OAuth2ResourceServerProperties properties) throws Exception { SSLUtil.turnOffSslChecking(); // 构造自定义RestTemplate,使用我们配置的无校验SSL上下文 RestTemplate restTemplate = new RestTemplate(new SimpleClientHttpRequestFactory() { @Override protected void prepareConnection(HttpURLConnection connection, String httpMethod) { if (connection instanceof HttpsURLConnection httpsConn) { httpsConn.setHostnameVerifier((hostname, session) -> true); // 直接指定无校验SocketFactory,避免依赖全局配置 httpsConn.setSSLSocketFactory(SSLContext.getInstance("SSL").getSocketFactory()); } } }); // 用自定义RestTemplate拉取OIDC配置并构造JwtDecoder return JwtDecoders.fromIssuerLocation(properties.getJwt().getIssuerUri()) .restOperations(restTemplate); } }
生产环境正确做法
不要禁用SSL校验,而是将授权服务器的根证书导入到JDK的信任库中,同时保证证书的SAN字段包含你访问授权服务器使用的域名/IP,从根源解决问题。
内容的提问来源于stack exchange,提问作者Mulgard
相关产品推荐
相关产品推荐

