• Rest接口加Http单向认证


    背景:

      接到一个需求,客户要求某个模块的rest接口都得通过https访问,客户提供证书。

    步骤:

    Server端证书生成

      刚开始还没拿到客户的证书,所以通过jdk自带的keytools自己先生成了一个证书用来测试,同事称这个为自签名。

      C:Program FilesJavajdk1.8.0_191jreinkeytool.exe 也就是jdk这个自带的程序, 说说怎么用吧。楼主用的windows操作系统

      到JDK的JRE的bin目录下,执行以下命令生成服务端sslServer.p12文件

      keytool -genkey -v -alias sslServer -keyalg RSA -storetype PKCS12 -keystore F:httpsDemosslServer.p12

      注意这个-alias很重要,是证书的别名,后面用得到。sslServer.p12就是生成的自签名证书了,后面需要用客户的证书替换。

      

      看了一些博客的介绍,这个名字和姓氏得是主机的ip,不然会有问题,本着不踩坑的思想,照做就行。当然这里的密码是肯定要记住的。。

    Spring配置

    将sslServer.p12放在resource目录下,与application.properties同级

    # 单向认证开启(客户端校验服务端证书即可)

    server.ssl.key-store=classpath:sslServer.p12

    server.ssl.key-store-password=server

    server.ssl.key-alias=sslServer

    server.ssl.keyStoreType=JKS

     

    其中,server.ssl.key-store,server.ssl.key-store-password,server.ssl.key-alias需要根据客户提供的证书进行配置,

    server.ssl.key-store对应证书位置,目前是用自生成的签名,部署时需要替换客户证书

    server.ssl.key-store-password对应证书密码

    server.ssl.key-alias对应生成证书时的证书别名

    keyStoreType固定为JKS

     

    到这里为止,服务端做https的认证就已经结束了,很简单吧。可以使用postman测试一波,记得关掉setting里的ssl认证,设置成off

     

    客户端怎么认证

    由于楼主的项目是微服架构,本身也会调到这个应用的服务,因此所有调到的地方都得特殊处理下。

    https认证分两种,本文只介绍单向认证,即客户端无条件信任服务端的证书,因为我的需求就只要单向

    RestTemplate配置

    @Configuration

    public class RestTemplateConfiguration {

     

        @Value("${readTimeout}")

        private String readTimeout;

     

        @Value("${connectionTimeout}")

        private String connectionTimeout;

     

        @Bean("restTemplate")

        public RestTemplate createRestTemplate() {

     

            ObjectMapper objectMapper = new ObjectMapper();

            SimpleModule simpleModule = new SimpleModule();

            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

            objectMapper.setDateFormat(dateFormat);

            objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);

            simpleModule.addSerializer(Long.class, ToStringSerializer.instance);

            simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);

            objectMapper.registerModule(simpleModule);

     

            RestTemplate restTemplate = new RestTemplate();

            restTemplate.setRequestFactory(simpleClientHttpRequestFactory());

            List<HttpMessageConverter<?>> converters = new ArrayList<>();

            MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();

            jsonConverter.setObjectMapper(objectMapper);

            converters.add(jsonConverter);

            restTemplate.setMessageConverters(converters);

            return restTemplate;

        }

     

        private ClientHttpRequestFactory simpleClientHttpRequestFactory() {

            SslClientHttpRequestFactory factory = new SslClientHttpRequestFactory();

            factory.setConnectTimeout(Integer.parseInt(connectionTimeout));

            factory.setReadTimeout(Integer.parseInt(readTimeout));

            return factory;

    }

     

    自定义http请求类

    public class SslClientHttpRequestFactory extends SimpleClientHttpRequestFactory {

        /**

         * 区分https请求

         * @param connection

         * @param httpMethod

         * @throws IOException

         */

        @Override

        protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {

            if (connection instanceof HttpsURLConnection) {

                prepareHttpsConnection((HttpsURLConnection) connection);

            }

            super.prepareConnection(connection, httpMethod);

        }

        private void prepareHttpsConnection(HttpsURLConnection connection) {

            connection.setHostnameVerifier(new SkipHostnameVerifier());

            try {

                connection.setSSLSocketFactory(createSslSocketFactory());

            }

            catch (Exception ex) {

                // Ignore

            }

        }

        private SSLSocketFactory createSslSocketFactory() throws Exception {

            SSLContext context = SSLContext.getInstance("TLS");

            context.init(null, new TrustManager[]{new SkipX509TrustManager()}, new SecureRandom());

            return context.getSocketFactory();

        }

        /**

         * 忽略hostName校验,允许ip代替域名

         */

        private static class SkipHostnameVerifier implements HostnameVerifier {

            @Override

            public boolean verify(String s, SSLSession sslSession) {

                return true;

            }

        }

        /**

         * 信任所有证书

         */

        private static class SkipX509TrustManager implements X509TrustManager {

            @Override

            public X509Certificate[] getAcceptedIssuers() {

                return null;

            }

            @Override

            public void checkClientTrusted(X509Certificate[] chain, String authType) {

            }

            @Override

            public void checkServerTrusted(X509Certificate[] chain, String authType) {

            }

        }

    }

    然后统一用这个restTemplate去调那个模块的https的服务就ok了,restTemplate怎么调不介绍,基本操作。

    .crt和.key导成.p12格式的证书

    你以为开发完了?没这么简单。

    上面说到我们的证书使用的是自签名证书,但客户提供的证书可能并不是你想的.p12格式的,可能是一个.crt格式的证书加上一个.key格式的私钥。

    这时候需要将.crt和.key导成.p12格式的证书。这里就要用到openssl工具了,怎么下载安装不介绍。

    执行以下openssl指令导出.p12格式的证书

    openssl pkcs12 -export -in test.crt -inkey test.key -passin pass:123456 -password pass:123456 -name server -out server.p12

    其中test.crt为客户提供的crt证书
    test.key 为客户提供的私钥文件
    -passin pass:123456代表给私钥设置密码123456
    -password pass:123456 代表给p12文件设置密码123456
    -name server 代表导出的证书别名为server
    server.p12 为导出的证书文件

    则对应的服务器端配置如下
    server.ssl.enabled=true
    server.ssl.key-store=classpath:server.p12
    server.ssl.key-store-password=123456
    server.ssl.key-alias=server
    server.ssl.key-store-type=JKS

    https服务端和客户端的单向认证到此为止。

     

  • 相关阅读:
    求列表中指定元素的位置
    Hash_P1026毒药?解药?
    Hash_集合
    bzoj1483: [HNOI2009]梦幻布丁
    bzoj1724: [Usaco2006 Nov]Fence Repair 切割木板
    容斥原理
    bzoj1042: [HAOI2008]硬币购物
    [Noi2016十连测第五场]二进制的世界
    NOI2016模拟赛Zbox loves stack
    bzoj2038: [2009国家集训队]小Z的袜子(hose)
  • 原文地址:https://www.cnblogs.com/sulishihupan/p/13170621.html
Copyright © 2020-2023  润新知