【原创】SpringBoot实现使用httpclient配合restTemplate验证服务器与服务器之间调用接口的TLS/SSL证书双向认证(精简版,附真实项目代码)

192 篇文章 13 订阅
17 篇文章 4 订阅

我的项目是服务器与服务器之间调接口,调用是需要ssl证书双向认证的。

yml中配置:

## 证书双向认证配置(本系统作为客户端)
client:
  ssl:
    abs:
      #    jks与pkcs12(即pfx)都可以。type不区分大小写
      #    path: ssl/abs/abs@aaa.abc.com.jks
      #    type: JKS
      path: ssl/abs/abs@aaa.abc.com.pfx
      type: PKCS12
      password: 123456
      # ssl双向认证开关
      auth: true

config/RestTemplateConfig.java中:

package com.abc.config;

import lombok.Data;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.cert.CertificateException;

@Data
@Configuration
@ConfigurationProperties(prefix="client.ssl.abs")
public class RestTemplateConfig {

    private String path;
    private String type;
    private String password;
    private boolean auth;

    @Bean
    public RestTemplate getRestTemplate() throws CertificateException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException, IOException {

        // https使用
        RestTemplate restTemplate = new RestTemplate(getHttpRequestFactory());

        // http使用
        // RestTemplate restTemplate = new RestTemplateBuilder().setConnectTimeout(Duration.ofSeconds(1000)).setReadTimeout(Duration.ofSeconds(5000)).build();

        //TODO 还是未解决乱码
        //解决中文乱码方式一
        restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
        //解决中文乱码方式二
        //restTemplate.getMessageConverters().set(1,new MappingJackson2HttpMessageConverter());
        //StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
        //stringHttpMessageConverter.setWriteAcceptCharset(true);

        return restTemplate;
    }

    public HttpComponentsClientHttpRequestFactory getHttpRequestFactory() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException {
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setConnectTimeout(1000);
        requestFactory.setReadTimeout(5000);
        if(auth) {
            requestFactory.setHttpClient(getHttpClient());
        }
        return requestFactory;
    }

    /**
     *
     * 开发环境中,不一定会有域名,因此可能会造成证书域名和真实服务器IP无法匹配而校验失败,提供两种解决方案:
     *
     * 方式一(不验证服务端证书):
     *      因此在开发环境中,客户端需要加上这么一段配置重写的verify方法,用来跳过服务端证书校验。
     *      代码:
     *          TrustStrategy acceptingTrustStrategy = (X509Certificate[] x509Certificates, String s) -> true;
     *          //配置信任链
     *          SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy);
     *
     * 方式二(推荐)(仅不验证服务端的证书与域名匹配,其它服务端证书项正常校验):
     *      在SSLConnectionSocketFactory中加上new NoopHostnameVerifier()跳过验证。
     *      (
     *          说明:但是若采取方式二,就需要预先使用keytool工具导入证书到'jdk1.8.0_231/jre/lib/security/cacerts'中
     *          注意:有证书链要导入整个证书链
     *          命令(在jdk的security下执行,cacerts通用默认密码是changeit):keytool -import -keystore cacerts -alias CA_SUB -file ca_root_and_sub.crt
     *       )
     *
     */
    public CloseableHttpClient getHttpClient() throws KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException, KeyManagementException, UnrecoverableKeyException {
        char[] password = this.password.toCharArray();
        KeyStore keyStore = KeyStore.getInstance(type);
        InputStream in = this.getClass().getClassLoader().getResourceAsStream(path);
        // 加载classpath下的文件要使用getClass().getClassLoader().getResourceAsStream(***),而不能用getClass().getResourceAsStream(***),获取不到
        // keyStore.load(new FileInputStream(new File(path)), password);
        keyStore.load(in, password);

        // TrustStrategy acceptingTrustStrategy = (X509Certificate[] x509Certificates, String s) -> true;
        SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                // 配置信任链,用于跳过验证服务器证书
                //.loadTrustMaterial(null, acceptingTrustStrategy)
                .loadKeyMaterial(keyStore, password)
                .build();

        // 单纯不校验域名证书匹配(NoopHostnameVerifier)
        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());

        // 校验域名证书匹配
        // SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
        // 或
        // SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(
        //        sslContext,
        //        // new String[]{"TLSv1","TLSv1.1","TLSv1.2"},
        //        // new String[]{"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384"},
        //        SSLConnectionSocketFactory.getDefaultHostnameVerifier());

        return HttpClients.custom().setSSLSocketFactory(csf).build();
    }

}

重点在于其中的getHttpClient()方法:

开发环境中,不一定会有域名,因此可能会造成证书域名和真实服务器IP无法匹配而校验失败,提供两种解决方案:

方式一(不验证服务端证书):

在开发环境中,客户端需要加上这么一段配置重写的verify方法,用来跳过服务端证书校验。

代码:
TrustStrategy acceptingTrustStrategy = (X509Certificate[] x509Certificates, String s) -> true;
//配置信任链
SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy);

 方式二(推荐)(仅不验证服务端的证书与域名匹配,其它服务端证书项正常校验):

在SSLConnectionSocketFactory中加上new NoopHostnameVerifier()跳过验证。

说明:
但是若采取方式二,就需要预先使用keytool工具导入证书到“jdk1.8.0_231/jre/lib/security/cacerts”中

注意:
有证书链要导入整个证书链

命令(在jdk的security下执行,cacerts通用默认密码是changeit):
① 先进入$JAVA_HOME/jre/lib/security/文件夹中
② keytool -import -keystore cacerts -alias CA_SUB -file ca_root_and_sub.crt
或linux中直接执行如下命令:
keytool -import -keystore $JAVA_HOME/jre/lib/security/cacerts -alias CA_SUB -file ca_root_and_sub.crt


参考自本人另一篇文章:配置SpringBoot实现使用httpclient配合restTemplate验证服务器与服务器之间调用接口的TLS/SSL证书双向认证(精辟!亲测可用!附真实项目代码)_HD243608836的博客-CSDN博客_springboot配置tls

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值