操作场景
本文以 provider-demo 和 consumer-demo 两个工程为例为您介绍 TSF 应用配置 http2 的操作方法。
前提条件
下载 TSF Demo 工程(springboot 版本2.0+,tomcat 版本8.5+)
说明
推荐使用 1.29.0-Finchley-RELEASE。
本文以 springboot-2.0.9.RELEASE 和 tomcat-8.5.56 为例。
操作步骤
步骤1:制作 SSL 证书
通过 JDK 自带的 keytool 执行以下命令:
keytool -genkey -alias tomcat -keyalg RSA -keystore ./keystore.jks -storepass 123456
执行成功后当前目录下会生成 keystore.jks 证书文件。
![](https://qcloudimg.tencent-cloud.cn/image/document/03fa785fe82138e3c6d105dba996221e.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/03fa785fe82138e3c6d105dba996221e.png)
步骤2:改造 provider-demo 工程
1. 复制 jks 证书文件到 provider-demo 工程的 resources 目录下。
![](https://qcloudimg.tencent-cloud.cn/image/document/798995795a43031db0e71055184a4c67.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/798995795a43031db0e71055184a4c67.png)
2. 修改 spring 配置文件,在 bootstrap.yaml 文件中增加如下配置。
![](https://qcloudimg.tencent-cloud.cn/image/document/cefc788284263d880d3b3470999e4d22.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/cefc788284263d880d3b3470999e4d22.png)
server.http2.enabled=trueserver.ssl.key-store=classpath:keystore.jksserver.ssl.key-store-password: 123456
3. 启动 provider-demo,浏览器访问 ![](https://qcloudimg.tencent-cloud.cn/image/document/397fe904844ce2c314a5137c8505ef74.png)
只需单击任意空白处,键入“thisisunsafe”即可。
![](https://qcloudimg.tencent-cloud.cn/image/document/9a4b0e2c448be705d986c4c39376b7cf.png)
打开 Console,Protocol 的值为 h2 表示配置成功。
![](https://qcloudimg.tencent-cloud.cn/image/document/51595eca8661ce7625cc54713479efb7.png)
参见资料:
https://127.0.0.1:18081/echo/1
。
Chrome 可能会提示“您的连接不是私密连接”。
![](https://qcloudimg.tencent-cloud.cn/image/document/397fe904844ce2c314a5137c8505ef74.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/9a4b0e2c448be705d986c4c39376b7cf.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/51595eca8661ce7625cc54713479efb7.png)
Spring 配置 http2 文档:howto-configure-http2
Tomcat 版本差异文档:whichversion
4. 开启 http 访问端口(可选)。
由于此时只能通过 https 方式访问,可加入 Tomcat 配置开放新端口来支持 http 访问。
4.1 在启动类中增加 Bean。
![](https://qcloudimg.tencent-cloud.cn/image/document/c1120f0d2e65b8e882b7a63043209dd7.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/c1120f0d2e65b8e882b7a63043209dd7.png)
package com.tsf.demo.provider;import org.apache.catalina.connector.Connector;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;import org.springframework.boot.web.servlet.server.ServletWebServerFactory;import org.springframework.cloud.openfeign.EnableFeignClients;import org.springframework.context.annotation.Bean;import org.springframework.tsf.annotation.EnableTsf;@SpringBootApplication@EnableFeignClients // 使用Feign微服务调用时请启用@EnableTsfpublic class ProviderApplication {@Value("${http.port}")private Integer port;/*** Tomcat增加支持http访问**/@Beanpublic ServletWebServerFactory servletContainer() {TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);// connector.setSecure(false);// connector.setScheme("http");connector.setPort(port);tomcat.addAdditionalTomcatConnectors(connector);return tomcat;}public static void main(String[] args) {SpringApplication.run(ProviderApplication.class, args);}}
5. 在 Bootstrap.yml 配置文件中增加自定义配置。
![](https://qcloudimg.tencent-cloud.cn/image/document/24c3db4426b9cd8b47d569b164a01125.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/24c3db4426b9cd8b47d569b164a01125.png)
http.port=18082
6. 重启 provider-demo,浏览器访问 ![](https://qcloudimg.tencent-cloud.cn/image/document/a3c37188770c8785ce2aa16e509e69c8.png)
此时 provider-demo 完成支持 https 和 http(通过不同端口访问),其中 https 访问时使用 http2 协议。
参见资料:Spring 配置 Tomcat 代码示例:SampleTomcatTwoConnectorsApplication.java。
http://127.0.0.1:18082/echo/1
。
![](https://qcloudimg.tencent-cloud.cn/image/document/a3c37188770c8785ce2aa16e509e69c8.png)
步骤3:改造 consumer-demo 工程
1. proxy 中的 @FeignClient 注解需指定 https 方式访问。
![](https://qcloudimg.tencent-cloud.cn/image/document/b5812723d0f7eecb42f8e526794a08c7.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/b5812723d0f7eecb42f8e526794a08c7.png)
@FeignClient(name = "https://provider-demo")
2. RestTemplate 同理。
![](https://qcloudimg.tencent-cloud.cn/image/document/80142419ec94ebe4da1e29c9eb97647e.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/80142419ec94ebe4da1e29c9eb97647e.png)
restTemplate.getForObject("https://provider-demo/echo/" + str, String.class);
3. 通过注入不同的 bean 选择是否使用SSL证书认证(以下步骤二选一):
1. 复制 jks 证书文件到 consumer-demo 工程的 resources 目录。
2. spring 配置文件中增加自定义配置。
![](https://qcloudimg.tencent-cloud.cn/image/document/d2fc9214dd1e9db2e386f63efc24149f.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/d2fc9214dd1e9db2e386f63efc24149f.png)
test-ssl-config.key-store=classpath:keystore.jkstest-ssl-config.key-store-password: 123456
3. 修改 bean(restTemplate、feignClient)
![](https://qcloudimg.tencent-cloud.cn/image/document/28f974e63c0a88fbacb67e4966ba440e.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/28f974e63c0a88fbacb67e4966ba440e.png)
package com.tsf.demo.consumer;import feign.Client;import org.apache.http.conn.ssl.NoopHostnameVerifier;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.conn.ssl.TrustSelfSignedStrategy;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.ssl.SSLContexts;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.cloud.netflix.ribbon.SpringClientFactory;import org.springframework.cloud.openfeign.EnableFeignClients;import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;import org.springframework.context.annotation.Bean;import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;import org.springframework.tsf.annotation.EnableTsf;import org.springframework.util.ResourceUtils;import org.springframework.web.client.AsyncRestTemplate;import org.springframework.web.client.RestTemplate;import javax.net.ssl.*;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.security.*;import java.security.cert.CertificateException;@SpringBootApplication@EnableFeignClients // 使用Feign微服务调用时请启用@EnableTsfpublic class ConsumerApplication {@Value("${test-ssl-config.key-store}")private String file;@Value("${test-ssl-config.key-store-password}")private String password;@LoadBalanced@Beanpublic RestTemplate restTemplate() {SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(getSSLSocket(file, password),new String[]{"TLSv1"},null,NoopHostnameVerifier.INSTANCE);CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();requestFactory.setHttpClient(httpClient);return new RestTemplate(requestFactory);}@Beanpublic Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) {return new LoadBalancerFeignClient(new Client.Default(getSSLSocket(file, password).getSocketFactory(), (hostname, session) -> true), cachingFactory, clientFactory);}public static SSLContext getSSLSocket(String file, String password) {SSLContext sslContext = null;try {KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());InputStream keyStoreInput = new FileInputStream(ResourceUtils.getFile(file));keyStore.load(keyStoreInput, password.toCharArray());KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());InputStream trustStoreInput = new FileInputStream(ResourceUtils.getFile(file));trustStore.load(trustStoreInput, null);sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, password.toCharArray()).loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();} catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | CertificateException | IOException | UnrecoverableKeyException e) {e.printStackTrace();}return sslContext;}@LoadBalanced@Beanpublic AsyncRestTemplate asyncRestTemplate() {return new AsyncRestTemplate();}public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}}
只需要修改 bean(restTemplate、feignClient)
![](https://qcloudimg.tencent-cloud.cn/image/document/ef6607a9bcfa6463235ea6685234854c.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/ef6607a9bcfa6463235ea6685234854c.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/d452574f4f4cf2f814bfa4280eb1bdec.png)
![](https://qcloudimg.tencent-cloud.cn/image/document/d452574f4f4cf2f814bfa4280eb1bdec.png)
说明
与第一种方式的差异在 getSSLSocket() 方法。
```javapackage com.tsf.demo.consumer;import feign.Client;import org.apache.http.conn.ssl.NoopHostnameVerifier;import org.apache.http.conn.ssl.SSLConnectionSocketFactory;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.cloud.netflix.ribbon.SpringClientFactory;import org.springframework.cloud.openfeign.EnableFeignClients;import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;import org.springframework.context.annotation.Bean;import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;import org.springframework.tsf.annotation.EnableTsf;import org.springframework.web.client.AsyncRestTemplate;import org.springframework.web.client.RestTemplate;import javax.net.ssl.*;import java.security.*;import java.security.cert.X509Certificate;@SpringBootApplication@EnableFeignClients // 使用Feign微服务调用时请启用@EnableTsfpublic class ConsumerApplication {@LoadBalanced@Beanpublic RestTemplate restTemplate() {SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(getSSLSocket(),new String[]{"TLSv1"},null,NoopHostnameVerifier.INSTANCE);CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();requestFactory.setHttpClient(httpClient);return new RestTemplate(requestFactory);}@Beanpublic Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) {return new LoadBalancerFeignClient(new Client.Default(getSSLSocket().getSocketFactory(), (hostname, session) -> true), cachingFactory, clientFactory);}public static SSLContext getSSLSocket() {SSLContext sslContext = null;try {sslContext = SSLContext.getInstance("TLS");X509TrustManager tm = new X509TrustManager() {@Overridepublic void checkClientTrusted(X509Certificate[] chain, String authType) {}@Overridepublic void checkServerTrusted(X509Certificate[] chain, String authType) {}@Overridepublic X509Certificate[] getAcceptedIssuers() {return null;}};sslContext.init(null, new TrustManager[]{tm}, null);} catch (NoSuchAlgorithmException | KeyManagementException e) {e.printStackTrace();}return sslContext;}@LoadBalanced@Beanpublic AsyncRestTemplate asyncRestTemplate() {return new AsyncRestTemplate();}public static void main(String[] args) {SpringApplication.run(ConsumerApplication.class, args);}}```
4. 验证结果。
4.1 启动 consumer-demo,浏览器访问 ![](https://qcloudimg.tencent-cloud.cn/image/document/3c737cb479bb9ca0f8529a17da2cf4b6.png)
http://127.0.0.1:18083/echo-feign/123
。
![](https://qcloudimg.tencent-cloud.cn/image/document/3c737cb479bb9ca0f8529a17da2cf4b6.png)
4.2 浏览器访问 ![](https://qcloudimg.tencent-cloud.cn/image/document/e492965349d1b4012c34833932f33912.png)
http://127.0.0.1:18083/echo-rest/123
。
![](https://qcloudimg.tencent-cloud.cn/image/document/e492965349d1b4012c34833932f33912.png)