zoukankan      html  css  js  c++  java
  • RestTemplate发送HTTP、HTTPS请求

    RestTemplate 使用总结

     

    场景:

    认证服务器需要有个 http client 把前端发来的请求转发到 backend service, 然后把 backend service 的结果再返回给前端,服务器本身只做认证功能。

    遇到的问题:

    • 长连接以保证高性能。RestTemplate 本身也是一个 wrapper 其底层默认是 SimpleClientHttpRequestFactory ,如果要保证长连接, HttpComponentsClientHttpRequestFactory 是个更好的选择,它不仅可以控制能够建立的连接数还能细粒度的控制到某个 server 的连接数,非常方便。在默认情况下,RestTemplate 到某个 server 的最大连接数只有 2, 一般需要调的更高些,最好等于 server 的 CPU 个数

    • access_token 不应传到 backend service. backend service 之间通信不需要 token,因为到这些服务的请求都是已经认证过的,是可信赖的用户发出的请求。因此转发请求时要把 parameter 从 request url 中删掉。删除 parameter 说难不难,说简单其实还有点麻烦,网上有一个 UrlEncodedQueryString 可以参考下,它封装了很多函数,其中就包括从url 中摘掉指定 header

    • 请求的 HttpMethod 问题。 HttpMethod 有很多种,http client 不应该对每种 Http method 都单独处理,所以应选用 RestTemplate 的 exchange 方法。exchange 方法要求给出 RequestBody 参数,而对于 Get 请求,这部分往往为空,所以我们要在 controller 中声明 @RequestBody(required = false) String body

    • exchange 的返回值和 controller 的返回值。Restful API 一般都是返回 json 的,所以最简单的是 exchange 和 controller 直接返回 String,但是返回 String 会有很多问题: 首先是如果某些 API 返回的是图片,那么这个 client 就傻掉了,需要为图片接口专门写 API,此外如果 backend service 返回的是 Gzip,那么此 client 必须对 gzip 先解压缩再返回请求者,如果不解压缩的话,相当于对着 gzip 数据做了到 String 类型的强制转换,使得请求者拿到的数据无法解析,所以最好的返回值是 byte[]。对于那种比较大的 json 返回值,省去了对 String 的类型转换后还能带来很大的性能提升

    • 关于返回值是 byte[] 还是 ResponseEntity<byte[]> 的问题。我觉得还是 ResponseEntity<byte[]> 好些,因为它就是 backend service 的结果。如果返回 byte[] 的话,还要对 HttpServletResponse 的 Header 进行修改,设置 Content-type, Content-encoding 等等。

      https://www.cnblogs.com/xinsheng/p/5546221.html


    Spring Boot忽略https证书:No subject alternative names present

    springboot--resttemplate访问https请求


    https://stackoverflow.com/questions/17619871/access-https-rest-service-using-spring-resttemplate






    前面我们介绍了如何使用Apache的HttpClient发送HTTP请求,这里我们介绍Spring的Rest客户端(即:RestTemplate)
    如何发送HTTP、HTTPS请求。注:HttpClient如何发送HTTPS请求,有机会的话也会再给出示例。

    声明:本人一些内容摘录自其他朋友的博客,链接在本文末给出!

    基础知识
             微服务都是以HTTP接口的形式暴露自身服务的,因此在调用远程服务时就必须使用HTTP客户端。我们可以使用JDK原生的URLConnection、Apache的Http Client、Netty的异步HTTP Client,最方便、最优雅的Feign, Spring的RestTemplate等。

    RestTemplate简述
            RestTemplate是Spring提供的用于访问Rest服务(Rest风格、Rest架构)的客户端。

            RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。

    调用RestTemplate的默认构造函数,RestTemplate对象在底层通过使用java.net包下的实现创建HTTP 请求;我们也可以通过使用ClientHttpRequestFactory指定不同的请求方式:


     

    ClientHttpRequestFactory接口主要提供了两种实现方式:


            1.常用的一种是SimpleClientHttpRequestFactory,使用J2SE提供的方式(既java.net包提供的方式)创建底层
                的Http请求连接。

             2.常用的另一种方式是使用HttpComponentsClientHttpRequestFactory方式,底层使用HttpClient访问远程的
                 Http服务,使用HttpClient可以配置连接池和证书等信息。

    软硬件环境: Windows10、Eclipse、JDK1.8、SpringBoot

    准备工作:引入相关依赖

    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>



    HTTP之GET请求(示例)

    import java.io.UnsupportedEncodingException;
    import java.net.URI;
    import java.net.URLEncoder;
    import java.nio.charset.StandardCharsets;
    import java.util.List;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.http.HttpEntity;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpMethod;
    import org.springframework.http.ResponseEntity;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.converter.StringHttpMessageConverter;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.web.client.RestTemplate;
    
    import com.google.gson.Gson;
    
    /**
    * 单元测试
    *
    * @author JustryDeng
    * @DATE 2018年9月7日 下午6:37:05
    */
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class AbcHttpsTestApplicationTests {
    
    /**
    * RestTemplate 发送 HTTP GET请求 --- 测试
    * @throws UnsupportedEncodingException 
    *
    * @date 2018年7月13日 下午4:18:50
    */
    @Test
    public void doHttpGetTest() throws UnsupportedEncodingException {
    // -------------------------------> 获取Rest客户端实例
    RestTemplate restTemplate = new RestTemplate();
    
    // -------------------------------> 解决(响应数据可能)中文乱码 的问题
    List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
    converterList.remove(1); // 移除原来的转换器
    // 设置字符编码为utf-8
    HttpMessageConverter<?> converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
    converterList.add(1, converter); // 添加新的转换器(注:convert顺序错误会导致失败)
    restTemplate.setMessageConverters(converterList);
    
    // -------------------------------> (选择性设置)请求头信息
    // HttpHeaders实现了MultiValueMap接口
    HttpHeaders httpHeaders = new HttpHeaders();
    // 给请求header中添加一些数据
    httpHeaders.add("JustryDeng", "这是一个大帅哥!");
    
    // -------------------------------> 注:GET请求 创建HttpEntity时,请求体传入null即可
    // 请求体的类型任选即可;只要保证 请求体 的类型与HttpEntity类的泛型保持一致即可
    String httpBody = null;
    HttpEntity<String> httpEntity = new HttpEntity<String>(httpBody, httpHeaders);
    
    // -------------------------------> URI
    StringBuffer paramsURL = new StringBuffer("http://127.0.0.1:9527/restTemplate/doHttpGet");
    // 字符数据最好encoding一下;这样一来,某些特殊字符才能传过去(如:flag的参数值就是“&”,不encoding的话,传不过去)
    paramsURL.append("?flag=" + URLEncoder.encode("&", "utf-8"));
    URI uri = URI.create(paramsURL.toString());
    
    // -------------------------------> 执行请求并返回结果
    // 此处的泛型 对应 响应体数据 类型;即:这里指定响应体的数据装配为String
    ResponseEntity<String> response = 
    restTemplate.exchange(uri, HttpMethod.GET, httpEntity, String.class);
    
    // -------------------------------> 响应信息
    //响应码,如:401、302、404、500、200等
    System.err.println(response.getStatusCodeValue());
    Gson gson = new Gson();
    // 响应头
    System.err.println(gson.toJson(response.getHeaders()));
    // 响应体
    if(response.hasBody()) {
    System.err.println(response.getBody());
    }
    
    }
    
    }


    被http请求的对应的方法逻辑为:

    注:我们也可以使用@RequestHeader()来获取到请求头中的数据信息,如:

    结果(效果)展示

    1.进行HTTP请求的方法获得响应后输出结果为:

    2.被HTTP请求的方法被请求后的输出结果为:

     
    HTTP之POST请求(示例)

    /**
    * RestTemplate 发送 HTTP POST请求 --- 测试
    * @throws UnsupportedEncodingException 
    *
    * @date 2018年9月8日 下午2:12:50
    */
    @Test
    public void doHttpPostTest() throws UnsupportedEncodingException {
    // -------------------------------> 获取Rest客户端实例
    RestTemplate restTemplate = new RestTemplate();
    
    // -------------------------------> 解决(响应数据可能)中文乱码 的问题
    List<HttpMessageConverter<?>> converterList = restTemplate.getMessageConverters();
    converterList.remove(1); // 移除原来的转换器
    // 设置字符编码为utf-8
    HttpMessageConverter<?> converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
    converterList.add(1, converter); // 添加新的转换器(注:convert顺序错误会导致失败)
    restTemplate.setMessageConverters(converterList);
    
    // -------------------------------> (选择性设置)请求头信息
    // HttpHeaders实现了MultiValueMap接口
    HttpHeaders httpHeaders = new HttpHeaders();
    // 设置contentType
    httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
    // 给请求header中添加一些数据
    httpHeaders.add("JustryDeng", "这是一个大帅哥!");
    
    // ------------------------------->将请求头、请求体数据,放入HttpEntity中
    // 请求体的类型任选即可;只要保证 请求体 的类型与HttpEntity类的泛型保持一致即可
    // 这里手写了一个json串作为请求体 数据 (实际开发时,可使用fastjson、gson等工具将数据转化为json串)
    String httpBody = "{"motto":"唉呀妈呀!脑瓜疼!"}";
    HttpEntity<String> httpEntity = new HttpEntity<String>(httpBody, httpHeaders);
    
    // -------------------------------> URI
    StringBuffer paramsURL = new StringBuffer("http://127.0.0.1:9527/restTemplate/doHttpPost");
    // 字符数据最好encoding一下;这样一来,某些特殊字符才能传过去(如:flag的参数值就是“&”,不encoding的话,传不过去)
    paramsURL.append("?flag=" + URLEncoder.encode("&", "utf-8"));
    URI uri = URI.create(paramsURL.toString());
    
    // -------------------------------> 执行请求并返回结果
    // 此处的泛型 对应 响应体数据 类型;即:这里指定响应体的数据装配为String
    ResponseEntity<String> response = 
    restTemplate.exchange(uri, HttpMethod.POST, httpEntity, String.class);
    
    // -------------------------------> 响应信息
    //响应码,如:401、302、404、500、200等
    System.err.println(response.getStatusCodeValue());
    Gson gson = new Gson();
    // 响应头
    System.err.println(gson.toJson(response.getHeaders()));
    // 响应体
    if(response.hasBody()) {
    System.err.println(response.getBody());
    }
    
    }

    被http请求的对应的方法逻辑为:

    注:我们也可以使用@RequestHeader()来获取到请求头中的数据信息,如:

    结果(效果)展示

    进行HTTP请求的方法获得响应后输出结果为:

    被HTTP请求的方法被请求后的输出结果为:

    HTTPS请求的准备工作
    HTTPS请求 = 超文本传输协议HTTP + 安全套接字层SSL。

    先给出等下需要用到的一个SimpleClientHttpRequestFactory的实现类

    /**
    * 声明:此代码摘录自https://blog.csdn.net/wltsysterm/article/details/80977455
    * 声明:关于Socket的相关知识,本人会在后面的闲暇时间进行学习整理,请持续关注博客更新
    *
    * @author JustryDeng
    * @DATE 2018年9月8日 下午4:34:02
    */
    public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory {
    
    @Override
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
    try {
    if (!(connection instanceof HttpsURLConnection)) {
    throw new RuntimeException("An instance of HttpsURLConnection is expected");
    }
    
    HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
    
    TrustManager[] trustAllCerts = new TrustManager[]{
    new X509TrustManager() {
    @Override
    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
    return null;
    }
    @Override
    public void checkClientTrusted(X509Certificate[] certs, String authType) {
    }
    @Override
    public void checkServerTrusted(X509Certificate[] certs, String authType) {
    }
    
    }
    };
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
    httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));
    
    httpsConnection.setHostnameVerifier(new HostnameVerifier() {
    @Override
    public boolean verify(String s, SSLSession sslSession) {
    return true;
    }
    });
    
    super.prepareConnection(httpsConnection, httpMethod);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    
    /**
    * We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
    * see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section)
    */
    // SSLSocketFactory用于创建 SSLSockets
    private static class MyCustomSSLSocketFactory extends SSLSocketFactory {
    
    private final SSLSocketFactory delegate;
    
    public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
    this.delegate = delegate;
    }
    
    // 返回默认启用的密码套件。除非一个列表启用,对SSL连接的握手会使用这些密码套件。
    // 这些默认的服务的最低质量要求保密保护和服务器身份验证
    @Override
    public String[] getDefaultCipherSuites() {
    return delegate.getDefaultCipherSuites();
    }
    
    // 返回的密码套件可用于SSL连接启用的名字
    @Override
    public String[] getSupportedCipherSuites() {
    return delegate.getSupportedCipherSuites();
    }
    
    
    @Override
    public Socket createSocket(final Socket socket, final String host, final int port, 
    final boolean autoClose) throws IOException {
    final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
    return overrideProtocol(underlyingSocket);
    }
    
    
    @Override
    public Socket createSocket(final String host, final int port) throws IOException {
    final Socket underlyingSocket = delegate.createSocket(host, port);
    return overrideProtocol(underlyingSocket);
    }
    
    @Override
    public Socket createSocket(final String host, final int port, final InetAddress localAddress, 
    final int localPort) throws
    IOException {
    final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
    return overrideProtocol(underlyingSocket);
    }
    
    @Override
    public Socket createSocket(final InetAddress host, final int port) throws IOException {
    final Socket underlyingSocket = delegate.createSocket(host, port);
    return overrideProtocol(underlyingSocket);
    }
    
    @Override
    public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, 
    final int localPort) throws
    IOException {
    final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
    return overrideProtocol(underlyingSocket);
    }
    
    private Socket overrideProtocol(final Socket socket) {
    if (!(socket instanceof SSLSocket)) {
    throw new RuntimeException("An instance of SSLSocket is expected");
    }
    ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1"});
    return socket;
    }
    }
    }
     

    HTTPS之GET请求
    说明:RestTemplate发送HTTPS与发送HTTP的代码,除了在创建RestTemplate时不一样以及协议不一样
             (一个URL是http开头,一个是https开头)外,其余的都一样。

    HTTP获取RestTemplate实例

    HTTPS获取RestTemplate实例

    给出具体HTTPS发送GET请求代码示例(HTTPS发送POST请求类比即可)


    注:HTTPS与HTTP的使用不同之处,在途中已经圈出了。

    注:上图中请求的https://tcc.taobao.com/cc/json/mobile_tel_segment.htm是阿里提供的一个简单查询手机信息的地址。

    运行该主函数,控制台打印出的结果为:

    注:如果用HTTP协议开头的URL去访问HTTPS开头的URL的话(这两个URL除了协议不同其它都相同),是访问不了的;除非服
         务端有相应的设置。

    注:发送HTTPS的逻辑代码是可以拿来发送HTTP的。但是根据我们写得HttpsClientRequestFactory类中的代码可知,会打
         印出异常(异常抛出后被catch了):

    如果用HTTPS访问HTTP时不想抛出异常,那么把对应的这个逻辑去掉即可。

    提示:“发送HTTPS的逻辑代码是可以拿来发送HTTP的”这句话的意思是:拿来做发HTTPS请求的逻辑,可以复用来作发HTTP请
             求的逻辑。并不是说说一个API能被HTTPS协议的URL访问,就一定能被HTTP协议的URL访问。

    HTTPS之GET请求
    注:关于HTTPS这里只给出了一个GET示例,使用HTTPS进行POST请求也是与HTTP进行POST请求也只是创建
          RestTemplate实例和协议不一样,其余的都一样;类比GET即可,这里就不再给出示例了。

    参考链接、摘录内容出处
            https://www.cnblogs.com/duanxz/p/3510622.html
            https://blog.csdn.net/wltsysterm/article/details/80977455
            https://blog.csdn.net/zhoumengshun/article/details/79100053
    如有不当之处,欢迎指正
    本次示例测试代码项目托管链接
            https://github.com/JustryDeng/PublicRepository
    本文已经被收录进《程序员成长笔记(三)》,笔者JustryDeng
    ---------------------
    作者:justry_deng
    来源:CSDN
    原文:https://blog.csdn.net/justry_deng/article/details/82531306
    版权声明:本文为博主原创文章,转载请附上博文链接!

    spring boot resttemplate 使用及支持https协议
    RestTemplate 使用
    添加httpclient依赖

    <!-- httpclient -->
    <dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    </dependency>

    配置类

    package net.fanci.stars.config;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.web.client.RestTemplateBuilder;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.Arrays;
    import java.util.List;
    
    /**
    * @author xxx
    * @create 2018/06/28 15:32
    * @description RestTemplate配置类:
    * 1.将 HttpClient 作为 RestTemplate 的实现,添加 httpclient 依赖即可
    * 2.设置响应类型和内容类型
    */
    @Configuration
    public class RestConfiguration {
    @Autowired
    private RestTemplateBuilder builder;
    
    @Bean
    public RestTemplate restTemplate() {
    return builder
    .additionalMessageConverters(new WxMappingJackson2HttpMessageConverter())
    .build();
    }
    
    class WxMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
    WxMappingJackson2HttpMessageConverter() {
    List<MediaType> mediaTypes = Arrays.asList(
    MediaType.TEXT_PLAIN,
    MediaType.TEXT_HTML,
    MediaType.APPLICATION_JSON_UTF8
    );
    setSupportedMediaTypes(mediaTypes);// tag6
    }
    }
    
    }

    使用方法(如:通过微信code获取token信息)

    @Autowired
    private RestTemplate restTemplate;
    
    /**
    * 获取access_token的完整信息
    *
    * @param code
    * @return
    */
    @Override
    public WechatAuthAccesstoken getWechatAuthAccesstoken(String code) {
    String url = ACCESS_TOKEN_URL + "appid=" + wechatData.getAppID() +
    "&secret=" + wechatData.getAppsecret() +
    "&code=" + code +
    "&grant_type=authorization_code";
    
    // com.alibaba.fastjson
    JSONObject jsonObject = restTemplate.getForObject(url, JSONObject.class);
    
    WechatAuthAccesstoken wechatAuthAccesstoken = new WechatAuthAccesstoken();
    if (jsonObject != null) {
    wechatAuthAccesstoken.setId(PayUtil.genUniqueKey());
    wechatAuthAccesstoken.setCreatedDate(DateTime.now().toDate());
    wechatAuthAccesstoken.setModifiedDate(DateTime.now().toDate());
    wechatAuthAccesstoken.setAccessToken((String) jsonObject.get("access_token"));
    DateTime now = DateTime.now();
    DateTime expired = now.plusSeconds((Integer) jsonObject.get("expires_in"));
    wechatAuthAccesstoken.setExpires(expired.toDate());
    wechatAuthAccesstoken.setRefreshToken(jsonObject.getString("refresh_token"));
    wechatAuthAccesstoken.setOpenid((String) jsonObject.get("openid"));
    wechatAuthAccesstoken.setScope(jsonObject.getString("scope"));
    int isOk = tokenMapper.insert(wechatAuthAccesstoken);
    if (isOk > 0) {
    logger.info("本地存储access_token信息成功");
    return wechatAuthAccesstoken;
    } else {
    logger.error("本地存储access_token信息失败");
    }
    } else {
    logger.error("获取access_token信息失败");
    }
    return null;
    }

    https支持
    配置类

    import org.springframework.http.client.SimpleClientHttpRequestFactory;
    
    import javax.net.ssl.*;
    import java.io.IOException;
    import java.net.HttpURLConnection;
    import java.net.InetAddress;
    import java.net.Socket;
    import java.security.cert.X509Certificate;
    
    /**
    * @author xxx
    * @create 2018/07/16 11:41
    * @description 创建 HttpsClientRequestFactory 以支持 RestTemplate 调用 https 请求
    */
    public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory {
    @Override
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
    try {
    if (!(connection instanceof HttpsURLConnection)) {
    throw new RuntimeException("An instance of HttpsURLConnection is expected");
    }
    
    HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
    
    TrustManager[] trustAllCerts = 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) {
    }
    
    }
    };
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
    httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));
    
    httpsConnection.setHostnameVerifier(new HostnameVerifier() {
    @Override
    public boolean verify(String s, SSLSession sslSession) {
    return true;
    }
    });
    
    super.prepareConnection(httpsConnection, httpMethod);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    
    /**
    * We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
    * see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section)
    */
    private static class MyCustomSSLSocketFactory extends SSLSocketFactory {
    
    private final SSLSocketFactory delegate;
    
    public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
    this.delegate = delegate;
    }
    
    @Override
    public String[] getDefaultCipherSuites() {
    return delegate.getDefaultCipherSuites();
    }
    
    @Override
    public String[] getSupportedCipherSuites() {
    return delegate.getSupportedCipherSuites();
    }
    
    @Override
    public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException {
    final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
    return overrideProtocol(underlyingSocket);
    }
    
    @Override
    public Socket createSocket(final String host, final int port) throws IOException {
    final Socket underlyingSocket = delegate.createSocket(host, port);
    return overrideProtocol(underlyingSocket);
    }
    
    @Override
    public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort) throws
    IOException {
    final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
    return overrideProtocol(underlyingSocket);
    }
    
    @Override
    public Socket createSocket(final InetAddress host, final int port) throws IOException {
    final Socket underlyingSocket = delegate.createSocket(host, port);
    return overrideProtocol(underlyingSocket);
    }
    
    @Override
    public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, final int localPort) throws
    IOException {
    final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
    return overrideProtocol(underlyingSocket);
    }
    
    private Socket overrideProtocol(final Socket socket) {
    if (!(socket instanceof SSLSocket)) {
    throw new RuntimeException("An instance of SSLSocket is expected");
    }
    ((SSLSocket) socket).setEnabledProtocols(new String[]{"TLSv1"});
    return socket;
    }
    }
    }

    使用类

    String message = null;
    String url = "https://ip:port/xxx";
    RestTemplate restTemplateHttps = new RestTemplate(new HttpsClientRequestFactory());
    
    List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
    StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
    messageConverters.add(stringHttpMessageConverter);
    restTemplateHttps.setMessageConverters(messageConverters);
    
    ResponseEntity<String> responseEntity = restTemplateHttps.postForEntity(url, paramsData, String.class);
    if (responseEntity != null && responseEntity.getStatusCodeValue() == 200) {
    message = responseEntity.getBody();
    }


    离殇一曲与谁眠: 要依赖有什么用全是JDK和springboot自带的包
    ---------------------
    作者:uanei
    来源:CSDN
    原文:https://blog.csdn.net/u013469944/article/details/84193792
    版权声明:本文为博主原创文章,转载请附上博文链接!

    1. 为什么使用HttpClient?

    一开始其实是考虑使用RestTemplate的,但遇到的难题自然是SSL认证以及NTLM的认证.以目前的RestTemplate还做不到NTLM认证.而且使用SSL认证的过程也是挺复杂的. 复杂的是:居然还是要借助HttpClient .

    @Bean
        public RestTemplate buildRestTemplate(List<CustomHttpRequestInterceptor> interceptors) throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        HttpComponentsClientHttpRequestFactory factory = new                                                    
            HttpComponentsClientHttpRequestFactory();
        factory.setConnectionRequestTimeout(requestTimeout);
        factory.setConnectTimeout(connectTimeout);
        factory.setReadTimeout(readTimeout);
        // https
        SSLContextBuilder builder = new SSLContextBuilder();
        builder.loadTrustMaterial(null, (X509Certificate[] x509Certificates, String s) -> true);
        SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(builder.build(), new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE);
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", new PlainConnectionSocketFactory())
                .register("https", socketFactory).build();
        PoolingHttpClientConnectionManager phccm = new PoolingHttpClientConnectionManager(registry);
        phccm.setMaxTotal(200);
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).setConnectionManager(phccm).setConnectionManagerShared(true).build();
        factory.setHttpClient(httpClient);
    
        RestTemplate restTemplate = new RestTemplate(factory);
        List<ClientHttpRequestInterceptor> clientInterceptorList = new ArrayList<>();
        for (CustomHttpRequestInterceptor i : interceptors) {
            ClientHttpRequestInterceptor interceptor = i;
            clientInterceptorList.add(interceptor);
        }
        restTemplate.setInterceptors(clientInterceptorList);
        
        return restTemplate;
    }
    复制代码

    2. 为什么要绕过SSL认证?

    至于为什么要绕过SSL认证,因为装证书的这些操作我并不会.同时也想试试能不能忽略这个证书认证调用接口.

    • 首先如果想绕过证书,都必先创建X509TrustManager这个对象并且重写它的方法.

    X509TrustManager该接口是一个用于Https的证书信任管理器,我们可以在这里添加我们的证书,让该管理器知道我们有那些证书是可以信任的.

    该接口会有三个方法:

    void checkClientTrusted(X509Certificate[] xcs, String str)
     
     void checkServerTrusted(X509Certificate[] xcs, String str)
     
      X509Certificate[] getAcceptedIssuers()
    复制代码
    1. 第一个方法checkClientTrusted.该方法检查客户端的证书,若不信任该证书则抛出异常。由于我们不需要对客户端进行认证,因此我们只需要执行默认的信任管理器的这个方法。JSSE中,默认的信任管理器类为TrustManager。

    2. 第二个方法checkServerTrusted.该方法检查 服务器 的证书,若不信任该证书同样抛出异常。通过自己实现该方法,可以使之信任我们指定的任何证书。在实现该方法时,也可以简单的不做任何处理,即一个空的函数体,由于不会抛出异常,它就会信任任何证书。

    3. 第三个方法getAcceptedIssusers,返回受信任的X509证书数组。

    而我们只需要重写这三个方法,并且不需要修改里面的内容.然后再交给HttpClient就可以实现绕过SSL认证了.

    X509TrustManager trustManager = new X509TrustManager() {
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return null;
    }
    
    @Override
    public void checkClientTrusted(X509Certificate[] xcs, String str) {
    }
    
    @Override
    public void checkServerTrusted(X509Certificate[] xcs, String str) {
    }
    
    SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.SSL);
    ctx.init(null, new TrustManager[]{trustManager}, null);
    
    //生成工厂
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
    
    //并注册到HttpClient中
    Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
    .register("http", PlainConnectionSocketFactory.INSTANCE)
    .register("https", socketFactory).build();
    
    HttpClientBuilder httpClientBuilder = HttpClients.custom().setConnectionManager(connectionManager);
    
    CloseableHttpClient httpClient = httpClientBuilder.build();
    复制代码

    回顾一下步骤:

    1. 创建X509TrustManager对象并重写方法.
    2. 创建SSLContext实例,并交到工厂管理.
    3. 注册到HttpClient中.通过ConnectionManager最后生成httpClient.

    3. 什么是NTLM?

    NTLM是NT LAN Manager的缩写,这也说明了协议的来源。NTLM 是 Windows NT 早期版本的标准安全协议,Windows 2000 支持 NTLM 是为了保持向后兼容。Windows 2000内置三种基本安全协议之一。

    NTLM的原理

    NTLM的工作原理描述

    其实我对这个了解得不是很深,因为遇上这种情况的感觉不会很多,所以网上的资源也不太多. 这里只是针对HttpClient遇上NTLM认证的情况详细描述一下.有兴趣的朋友可以通过以上的链接了解下.

    4. 如何使用HttpClient进行NTLM认证?

    这个查阅了官网的文档.官网也给出了解决方案.

    hc.apache.org/httpcompone…

    需要把这几个类编写一下.

    JCIFSEngine:

    public final class JCIFSEngine implements NTLMEngine {
    
        private static final int TYPE_1_FLAGS =
                NtlmFlags.NTLMSSP_NEGOTIATE_56 |
                        NtlmFlags.NTLMSSP_NEGOTIATE_128 |
                        NtlmFlags.NTLMSSP_NEGOTIATE_NTLM2 |
                        NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN |
                        NtlmFlags.NTLMSSP_REQUEST_TARGET;
    
    
        @Override
        public String generateType1Msg(final String domain, final String workstation) throws NTLMEngineException {
            final Type1Message type1Message = new Type1Message(TYPE_1_FLAGS, domain, workstation);
            return Base64.encode(type1Message.toByteArray());
        }
    
        @Override
        public String generateType3Msg(final String username, final String password,
                                       final String domain, final String workstation, final String challenge)
                throws NTLMEngineException {
            Type2Message type2Message;
            try {
                type2Message = new Type2Message(Base64.decode(challenge));
            } catch (final IOException exception) {
                throw new NTLMEngineException("Invalid NTLM type 2 message", exception);
            }
            final int type2Flags = type2Message.getFlags();
            final int type3Flags = type2Flags
                    & (0xffffffff ^ (NtlmFlags.NTLMSSP_TARGET_TYPE_DOMAIN | NtlmFlags.NTLMSSP_TARGET_TYPE_SERVER));
            final Type3Message type3Message = new Type3Message(type2Message, password, domain,
                    username, workstation, type3Flags);
            return Base64.encode(type3Message.toByteArray());
        }
    }
    复制代码

    JCIFSNTLMSchemeFactory:

    public class JCIFSNTLMSchemeFactory implements AuthSchemeProvider {
    
        public AuthScheme create(final HttpContext context){
            return new NTLMScheme(new JCIFSEngine());
        }
    }
    复制代码

    最后就在HttpClient注册:

    Registry<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
            .register(AuthSchemes.NTLM, new JCIFSNTLMSchemeFactory())
            .register(AuthSchemes.BASIC, new BasicSchemeFactory())
            .register(AuthSchemes.DIGEST, new DigestSchemeFactory())
            .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
            .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory())
            .build();
    CloseableHttpClient httpClient = HttpClients.custom()
            .setDefaultAuthSchemeRegistry(authSchemeRegistry)
            .build();
    复制代码

    最后就同时使用绕过SSL验证以及NTLM验证:

    private static PoolingHttpClientConnectionManager connectionManager;
    private static RequestConfig requestConfig;
    private static Registry<AuthSchemeProvider> authSchemeRegistry;
    private static Registry<ConnectionSocketFactory> socketFactoryRegistry;
    private static CredentialsProvider credsProvider;
    
    
    public void init() {
    try {
    X509TrustManager trustManager = new X509TrustManager() {
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }
    
    @Override
        public void checkClientTrusted(X509Certificate[] xcs, String str) {
        }
    
        @Override
        public void checkServerTrusted(X509Certificate[] xcs, String str) {
        }
    };
    SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.SSL);
    ctx.init(null, new TrustManager[]{trustManager}, null);
    SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx, NoopHostnameVerifier.INSTANCE);
    NTCredentials creds = new NTCredentials("用户名", "密码", "工作站(workstation)", "域名");
    credsProvider = new BasicCredentialsProvider();
    credsProvider.setCredentials(AuthScope.ANY, creds);
    socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", PlainConnectionSocketFactory.INSTANCE)
            .register("https", socketFactory).build();
    connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
    connectionManager.setMaxTotal(18);
    connectionManager.setDefaultMaxPerRoute(6);
    requestConfig = RequestConfig.custom()
            .setSocketTimeout(30000)
            .setConnectTimeout(30000)
            .build();
    authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
            .register(AuthSchemes.NTLM, new JCIFSNTLMSchemeFactory())
            .register(AuthSchemes.BASIC, new BasicSchemeFactory())
            .register(AuthSchemes.DIGEST, new DigestSchemeFactory())
            .register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory())
            .register(AuthSchemes.KERBEROS, new KerberosSchemeFactory())
            .build();
    } catch (Exception e) {
    e.printStackTrace();
    }
    }
    复制代码

    以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

    为你推荐:

    https://www.codercto.com/a/36829.html

  • 相关阅读:
    关于万门大学api接口反爬措施的解决
    磁盘
    汇编实验九
    汇编语言第六章-包含多个段的程序
    汇编实验四
    汇编实验二
    汇编语言实验四
    汇编语言第四章——第一个程序
    汇编语言第三章——寄存器(内存访问)
    汇编语言第二张寄存器
  • 原文地址:https://www.cnblogs.com/softidea/p/10663849.html
Copyright © 2011-2022 走看看