java客户端验证https连接(忽略证书验证和证书验证两种方式)

首先根据如下操作生成证书,配置springboot https,生成一个简单的https web服务

https://www.cnblogs.com/qq931399960/p/11889349.html

验证客户端pom依赖

</dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpclient</artifactId>
                <version>4.5.10</version>
            </dependency>
            <dependency>
                <groupId>org.apache.httpcomponents</groupId>
                <artifactId>httpcore</artifactId>
                <version>4.4.12</version>
            </dependency>

httpclient和httpcore版本要对应,否则可能会出现异常

验证方式包括跳过证书验证,也即是添加信任,就像浏览器访问自签名https服务时,页面会给出提示“您的链接不是私密连接”,点击了高级,继续前往即是对该服务添加了信任,可以继续访问该网站服务,另外一种方式就是通过服务器证书来验证,下面就直接上代码

跳过证书验证方式

package com.demo.bootdemo;

import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class SkipVerifyRestTemplateBuilder {

	private Logger logger = LoggerFactory.getLogger(SkipVerifyRestTemplateBuilder.class);

	// 初始化ssl resttemplate
	@Bean("skipVerifyRestTemplate")
	public RestTemplate skipVerifyRestTemplate() {
		RestTemplate rest = new RestTemplate();

		SSLConnectionSocketFactory buildSSLSocketFactory = null;
		try {
			buildSSLSocketFactory = this.buildSSLSocketFactory();
		} catch (Exception e) {
			logger.error("", e);
		}

		HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(
				HttpClients.custom().setSSLSocketFactory(buildSSLSocketFactory).build());

		factory.setConnectionRequestTimeout(1000);
		factory.setConnectTimeout(1000);
		
		rest.setRequestFactory(factory);
		return rest;
	}

	private SSLConnectionSocketFactory buildSSLSocketFactory() throws Exception {
		SSLContext sslContext = SSLContext.getInstance("SSL");
		// 设置信任证书(绕过TrustStore验证)
		sslContext.init(null, new TrustManager[] { new AuthX509TrustManager() }, null);
		HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

		SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext,
				new String[] { "TLSv1" }, null, new HostnameVerifier() {
					// hostname,默认返回true,不验证hostname
					@Override
					public boolean verify(String urlHostName, SSLSession session) {
						return true;
					}
				});
		return sslConnectionSocketFactory;
	}

	private class AuthX509TrustManager implements TrustManager, X509TrustManager {
		public X509Certificate[] getAcceptedIssuers() {
			return null;
		}

		public void checkServerTrusted(X509Certificate[] certs, String authType)
				throws java.security.cert.CertificateException {
			return;
		}

		public void checkClientTrusted(X509Certificate[] certs, String authType) throws CertificateException {
			return;
		}
	}
}

第二种跳过证书验证方式

package com.demo.bootdemo;

import java.io.IOException;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;

import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class SecondSkipVerifyRestTemplateBuilder {

    @Bean("secondSkipRestTemplate")
    public RestTemplate verifyCaRestTemplate() {
        RestTemplate rest = new RestTemplate();
        SSLConnectionSocketFactory ssLSocketFactory = null;
        try {
            ssLSocketFactory = sslFactory("PKCS12", "abc123");
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory(
                HttpClients.custom().setSSLSocketFactory(ssLSocketFactory).build());
        // 设置传递数据超时时长
        httpRequestFactory.setReadTimeout(1000);

        rest.setRequestFactory(httpRequestFactory);

        // 如果返回的数据非json则可能需要添加对应httpmessageconverter
        // Jaxb2RootElementHttpMessageConverter converter = new
        // Jaxb2RootElementHttpMessageConverter();
        //
        // List<MediaType> mediaTypeList = new ArrayList<>();
        // mediaTypeList.addAll(converter.getSupportedMediaTypes());
        // mediaTypeList.add(MediaType.TEXT_HTML);
        // converter.setSupportedMediaTypes(mediaTypeList);
        //
        // List<HttpMessageConverter<?>> list = new ArrayList<>();
        // list.add(converter);
        // rest.setMessageConverters(list);

        return rest;

    }

    public SSLConnectionSocketFactory sslFactory(String keyStoreType, String keyPassword) {
        SSLConnectionSocketFactory sslConnectionSocketFactory = null;
        try {
            SSLContext sslcontext = SSLContexts.custom()
                    // //忽略掉对服务器端证书的校验
                    .loadTrustMaterial(new TrustStrategy() {
                        @Override
                        public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                            return true;
                        }
                    }).build();
            sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
                    SSLConnectionSocketFactory.getDefaultHostnameVerifier());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return sslConnectionSocketFactory;
    }

}

根据证书验证

package com.demo.bootdemo;

import java.io.IOException;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.List;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;

import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.Resource;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

@Component
public class VerifyCaRestTemplateBuilder {

    private Logger logger = LoggerFactory.getLogger(VerifyCaRestTemplateBuilder.class);

    @Value("classpath:cert.p12")
    private Resource certFile;

    @Bean("verifyCaRestTemplate")
    public RestTemplate verifyCaRestTemplate() {
        RestTemplate rest = new RestTemplate();

        SSLConnectionSocketFactory ssLSocketFactory = null;
        try {
            ssLSocketFactory = sslFactory("PKCS12", "abc123");
        } catch (Exception e) {
            logger.error("", e);
        }

        HttpComponentsClientHttpRequestFactory httpRequestFactory = new HttpComponentsClientHttpRequestFactory(
                HttpClients.custom().setSSLSocketFactory(ssLSocketFactory).build());
        // 设置传递数据超时时长
        httpRequestFactory.setReadTimeout(1000);

        rest.setRequestFactory(httpRequestFactory);

        // 如果返回的数据非json则可能需要添加对应httpmessageconverter
        // Jaxb2RootElementHttpMessageConverter converter = new
        // Jaxb2RootElementHttpMessageConverter();
        //
        // List<MediaType> mediaTypeList = new ArrayList<>();
        // mediaTypeList.addAll(converter.getSupportedMediaTypes());
        // mediaTypeList.add(MediaType.TEXT_HTML);
        // converter.setSupportedMediaTypes(mediaTypeList);
        //
        // List<HttpMessageConverter<?>> list = new ArrayList<>();
        // list.add(converter);
        // rest.setMessageConverters(list);

        return rest;

    }

    public SSLConnectionSocketFactory sslFactory(String keyStoreType, String keyPassword) {
        SSLConnectionSocketFactory sslConnectionSocketFactory = null;
        try {
            KeyStore keyStore = null;
            try {
                keyStore = KeyStore.getInstance(keyStoreType);
                keyStore.load(certFile.getInputStream(), keyPassword.toCharArray());
            } catch (IOException e) {
                logger.error("", e);
            }

            HostnameVerifier hv = new HostnameVerifier() {
                @Override
                public boolean verify(String urlHostName, SSLSession session) {
                    // 如果需要验证https域名,可以在该处做判断,如果访问的hostname与判断不一致,则会出现如下异常
                    // if("localhost".equals(urlHostName)) {
                    // return true;
                    // }else {
                    // return false;
                    // }
                    // 此处不校验hostname,接收所有hostname,只是用于测试。
                    return true;
                }
            };

            SSLContext sslcontext = SSLContexts.custom()
                    .loadTrustMaterial(certFile.getFile(), keyPassword.toCharArray(), new TrustSelfSignedStrategy())
                    .loadKeyMaterial(keyStore, keyPassword.toCharArray()).build();
            sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null, hv);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return sslConnectionSocketFactory;
    }

}

 注:在上述HostnameVerifier 中,可以验证hostname有效性,如果无效,返回fase,则会出现类似以下异常

javax.net.ssl.SSLPeerUnverifiedException: Certificate for <localhost> doesn't match any of the subject alternative names: []

测试controller

package com.demo.bootdemo;

import javax.annotation.Resource;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;

@Controller
public class HttpsSendController {

    @Resource(name = "skipVerifyRestTemplate")
    private RestTemplate skipVerifyRestTemplate;

    @Resource(name = "verifyCaRestTemplate")
    private RestTemplate verifyCaRestTemplate;

    @Resource(name = "secondSkipRestTemplate")
    private RestTemplate secondSkipRestTemplate;

    @RequestMapping("/skip")
    @ResponseBody
    public String skipVerifyCert() {
        ResponseEntity<String> forEntity = skipVerifyRestTemplate.getForEntity("https://127.0.0.1:8443/test",
                String.class, new Object[] {});
        return forEntity.getBody();
    }

    @RequestMapping("/secondskip")
    @ResponseBody
    public String secondSkipVerifyCert() {
        ResponseEntity<String> forEntity = skipVerifyRestTemplate.getForEntity("https://127.0.0.1:8443/test",
                String.class, new Object[] {});
        return forEntity.getBody();
    }

    @RequestMapping("/verify")
    @ResponseBody
    public String verifyCert() {
        ResponseEntity<String> forEntity = verifyCaRestTemplate.getForEntity("https://127.0.0.1:8443/test",
                String.class, new Object[] {});
        return forEntity.getBody();
    }

}

可分别访问当前客户端的skip、secondskip、verify验证结果

 

posted @ 2019-11-21 10:50  大坑水滴  阅读(18230)  评论(0编辑  收藏  举报