首页 > 其他分享 >RestTemplate发送HTTP、HTTPS请求

RestTemplate发送HTTP、HTTPS请求

时间:2023-08-03 21:32:57浏览次数:28  
标签:HTTP String RestTemplate public HTTPS new import final

 

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 等等。



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

springboot--resttemplate访问https请求

<!--http请求包-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.3</version>
        </dependency>

 

import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
 
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
 
public class HttpClientUtils {
 
    public static CloseableHttpClient acceptsUntrustedCertsHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        HttpClientBuilder b = HttpClientBuilder.create();
 
        // setup a Trust Strategy that allows all certificates.
        //
        SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
                return true;
            }
        }).build();
        b.setSSLContext(sslContext);
 
        // don't check Hostnames, either.
        //      -- use SSLConnectionSocketFactory.getDefaultHostnameVerifier(), if you don't want to weaken
        HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
 
        // here's the special part:
        //      -- need to create an SSL Socket Factory, to use our weakened "trust strategy";
        //      -- and create a Registry, to register it.
        //
        SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslSocketFactory)
                .build();
 
        // now, we create connection-manager using our Registry.
        //      -- allows multi-threaded use
        PoolingHttpClientConnectionManager connMgr = new PoolingHttpClientConnectionManager( socketFactoryRegistry);
        connMgr.setMaxTotal(200);
        connMgr.setDefaultMaxPerRoute(100);
        b.setConnectionManager( connMgr);
 
        // finally, build the HttpClient;
        //      -- done!
        CloseableHttpClient client = b.build();
 
        return client;
    }
 
}

SpringBoot启动类添加配置

在启动类上配置RestTemplate(因为启动类也是配置类,比较方便) 

@Bean
  public RestTemplate httpsRestTemplate(HttpComponentsClientHttpRequestFactory httpsFactory) {
    RestTemplate restTemplate = new RestTemplate(httpsFactory);
    restTemplate.setErrorHandler(
        new ResponseErrorHandler() {
          @Override
          public boolean hasError(ClientHttpResponse clientHttpResponse) {
            return false;
          }
 
          @Override
          public void handleError(ClientHttpResponse clientHttpResponse) {
            // 默认处理非200的返回,会抛异常
          }
        });
    return restTemplate;
  }
 
  @Bean(name = "httpsFactory")
  public HttpComponentsClientHttpRequestFactory httpComponentsClientHttpRequestFactory()
      throws Exception {
    CloseableHttpClient httpClient = HttpClientUtils.acceptsUntrustedCertsHttpClient();
    HttpComponentsClientHttpRequestFactory httpsFactory =
        new HttpComponentsClientHttpRequestFactory(httpClient);
    httpsFactory.setReadTimeout(40000);
    httpsFactory.setConnectTimeout(40000);
    return httpsFactory;
  }

不出意外就可以愉快的访问https请求了!

---------------------
作者:别浪呀
来源:CSDN
原文:
版权声明:本文为博主原创文章,转载请附上博文链接!

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的实现类

/**
* 声明:此代码摘录自
* 声明:关于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://github.com/JustryDeng/PublicRepository本文已经被收录进《程序员成长笔记(三)》,笔者JustryDeng
---------------------
作者:justry_deng
来源:CSDN
原文:
版权声明:本文为博主原创文章,转载请附上博文链接!

 

 

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
原文:
版权声明:本文为博主原创文章,转载请附上博文链接!

 

 

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

场景:

认证服务器需要有个 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 等等。



标签:HTTP,String,RestTemplate,public,HTTPS,new,import,final
From: https://blog.51cto.com/u_15147537/6953472

相关文章

  • nps是一款轻量级、高性能、功能强大的内网穿透代理服务器。目前支持tcp、udp流量转发,
    nps  nps是一款轻量级、高性能、功能强大的内网穿透代理服务器。目前支持tcp、udp流量转发,可支持任何tcp、udp上层协议(访问内网网站、本地支付接口调试、ssh访问、远程桌面,内网dns解析等等……),此外还支持内网http代理、内网socks5代理、p2p等,并带有功能强大的web管理端。背景做微......
  • nginx记录分析网站响应慢的请求(ngx_http_log_request_speed)
    nginx模块ngx_http_log_request_speed可以用来找出网站哪些请求很慢,针对站点很多,文件以及请求很多想找出哪些请求比较慢的话,这个插件非常有效.作者的初衷是写给自己用的,用来找出站点中处理时间较长的请求,这些请求是造成服务器高负载的很大根源.日志记录之后,在使用perl脚本分析......
  • 猛戳进来!小白也会懂的HTTP代理授权方式大公开
    各位小伙伴们,大家有没有遇到过一种情况——你在网上冲浪时突然遭遇到一些恼人的限制?刚想看个爆笑视频,结果被告知“禁止访问”!今天我来给大家普及一下HTTP代理授权方式,帮助解决这个问题,让我们告别网络封禁!首先,我们先来了解一下什么是HTTP代理。简单来说,HTTP代理就像是你找了......
  • 中间人攻击 http与https的区别
    由一个视频引发的问题 https://v.douyin.com/iJJ9n2r7/ 中间人攻击  由于HTTP本身不具备加密的功能,所以也无法做到对通信整体(使用HTTP协议通信的请求和相应的内容)进行加密,即HTTP报文使用明文(未经过加密的报文)方式发送。(对应于信件使用的文字不加密)抓包:可以得到cookie......
  • 自建稳定高效的HTTP代理池技术分享
    如何自建稳定高效的HTTP代理池技术分享对于爬虫技术人员来说,自建HTTP代理池是提高爬虫效率和成功率的关键一环。本文将详细介绍如何构建稳定高效的自建HTTP代理池。通过本文,你将能够充分利用代理池的优势,顺利完成数据采集任务。一、选择合适的代理IP源首先,选择合适的代理IP源对于构......
  • 解决RestTemplate乱码问题
    先来处理后的效果图 代码部分 @Bean@LoadBalancedpublicRestTemplategetRestTemplate(){RestTemplatetemplate=newRestTemplate();template.getMessageConverters().set(1,newStringHttpMessageConverter(StandardCharsets.UTF_8));......
  • Go 语言中 net/http 标准库的初步使用
    1.概述Go中的net/http库提供了HTTP客户端和服务端的实现。也就是说net/http可以分为客户端和服务器端两部分,库中的结构和函数有些只支持客户端和服务器这两者中的一个,而有些则同时支持客户端和服务器,如下图所示:2.对web服务器的理解的示意图2.1.http服务器(只讨论......
  • HTTP代理是什么?比较好的HTTP代理商有哪些?
    什么是HTTP代理?HTTP代理是一种网络代理技术,主要作用是接收用户发送的HTTP请求,并将这些请求转发到目标服务器,在用户和目标服务器之间充当中介。HTTP代理在各个领域都有广泛的应用。数据抓取:HTTP代理可用于大规模的数据采集任务,实现高并发请求,帮助用户高效获取不同区域、大量的数......
  • The Report Viewer Web Control HTTP Handler has not been registered in the applic
     system.webServer内加<handlers><addname="ReportViewerWebControlHandler"preCondition="integratedMode"verb="*"path="Reserved.ReportViewerWebControl.axd"type="Microsoft.Reporting.WebForms.Htt......
  • 《http篇》通过Asp.Net Core实现http服务(未采用)
    实现http服务参考链接:https://blog.csdn.net/icoolno1/article/details/754705/概述和.NetFramework时代不同,Core是可以自承载的,也就说开发好的Web项目,可以打包成exe直接运行的,而不必放到IIS这样的环境中。接下来,我们写点代码,体验一下Asp.NetCore自带的Http功能。新建Asp.Net......