RestTemplate发送HTTP、HTTPS请求
场景:
认证服务器需要有个 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 的类型转换后还能带来很⼤的性能提升
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.fig.Registry;
import org.fig.RegistryBuilder;
import org.socket.ConnectionSocketFactory;
import org.socket.PlainConnectionSocketFactory;
import org.ssl.NoopHostnameVerifier;
import org.ssl.SSLConnectionSocketFactory;
import org.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder;
import javax.ssl.HostnameVerifier;
import javax.ssl.SSLContext;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import CertificateException;
import X509Certificate;
public class HttpClientUtils {
public static CloseableHttpClient acceptsUntrustedCertsHttpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
HttpClientBuilder b = ate();
// 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.
//      -- DefaultHostnameVerifier(), 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", SocketFactory())
.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请求了!
声明:本⼈⼀些内容摘录⾃其他朋友的博客,链接在本⽂末给出!
基础知识
微服务都是以HTTP接⼝的形式暴露⾃⾝服务的,因此在调⽤远程服务时就必须使⽤HTTP客户端。我们可以使⽤JDK原⽣的URLConnection、Apache的Http Client、Netty的异步HTTP Client,最⽅便、最优雅的Feign, Spring的RestTemplate等。
RestTemplate简述
RestTemplate是Spring提供的⽤于访问Rest服务(Rest风格、Rest架构)的客户端。
RestTemplate提供了多种便捷访问远程Http服务的⽅法,能够⼤⼤提⾼客户端的编写效率。
调⽤RestTemplate的默认构造函数,RestTemplate对象在底层通过使⽤java包下的实现创建HTTP 请求;我们也可以通过使⽤ClientHttpRequestFactory指定不同的请求⽅式:
ClientHttpRequestFactory接⼝主要提供了两种实现⽅式:
1.常⽤的⼀种是SimpleClientHttpRequestFactory,使⽤J2SE提供的⽅式(既java包提供的⽅式)创建底层
的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.URI;
import java.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.st.context.SpringBootTest;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.verter.HttpMessageConverter;
import org.verter.StringHttpMessageConverter;
import st.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;
le.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 = MessageConverters();
// 设置字符编码为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("127.0.0.1:9527/restTemplate/doHttpGet");
// 字符数据最好encoding⼀下;这样⼀来,某些特殊字符才能传过去(如:flag的参数值就是“&”,不encoding的话,传不过去) paramsURL.append("?flag=" + de("&", "utf-8"));
URI uri = String());
// -------------------------------> 执⾏请求并返回结果
// 此处的泛型对应响应体数据类型;即:这⾥指定响应体的数据装配为String
ResponseEntity<String> response =
// -------------------------------> 响应信息
//响应码,如:401、302、404、500、200等
Gson gson = new Gson();
// 响应头
// 响应体
if(response.hasBody()) {
}
}
}
被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 = MessageConverters();
/
/ 设置字符编码为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中添加⼀些数据
springboot其实就是spring
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("127.0.0.1:9527/restTemplate/doHttpPost");
// 字符数据最好encoding⼀下;这样⼀来,某些特殊字符才能传过去(如:flag的参数值就是“&”,不encoding的话,传不过去) paramsURL.append("?flag=" + de("&", "utf-8"));
URI uri = String());
// -------------------------------> 执⾏请求并返回结果
// 此处的泛型对应响应体数据类型;即:这⾥指定响应体的数据装配为String
ResponseEntity<String> response =
// -------------------------------> 响应信息
//响应码,如:401、302、404、500、200等
Gson gson = new Gson();
// 响应头
// 响应体
if(response.hasBody()) {
}
}
被http请求的对应的⽅法逻辑为:
注:我们也可以使⽤@RequestHeader()来获取到请求头中的数据信息,如:
结果(效果)展⽰
进⾏HTTP请求的⽅法获得响应后输出结果为:
被HTTP请求的⽅法被请求后的输出结果为:
HTTPS请求的准备⼯作
HTTPS请求 = 超⽂本传输协议HTTP + 安全套接字层SSL。
先给出等下需要⽤到的⼀个SimpleClientHttpRequestFactory的实现类
/**
* 声明:此代码摘录⾃blog.csdn/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 X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
SSLContext sslContext = Instance("TLS");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
httpsConnection.setSSLSocketFactory(new SocketFactory())); 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 acle/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() {
DefaultCipherSuites();
}
// 返回的密码套件可⽤于SSL连接启⽤的名字
@Override
public String[] getSupportedCipherSuites() {
SupportedCipherSuites();
}
@Override
public Socket createSocket(final Socket socket, final String host, final int port,
final boolean autoClose) throws IOException {
final Socket underlyingSocket = ateSocket(socket, host, port, autoClose);
return overrideProtocol(underlyingSocket);
}
@Override
public Socket createSocket(final String host, final int port) throws IOException {
final Socket underlyingSocket = ateSocket(host, port);
return overrideProtocol(underlyingSocket);
}
@Override

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。