操作场景
本文以 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 证书文件。
步骤2:改造 provider-demo 工程
1. 复制 jks 证书文件到 provider-demo 工程的 resources 目录下。
2. 修改 spring 配置文件,在 bootstrap.yaml 文件中增加如下配置。
server.http2.enabled=trueserver.ssl.key-store=classpath:keystore.jksserver.ssl.key-store-password: 123456
3. 启动 provider-demo,浏览器访问
https://127.0.0.1:18081/echo/1
。
Chrome 可能会提示“您的连接不是私密连接”。
只需单击任意空白处,键入“thisisunsafe”即可。
打开 Console,Protocol 的值为 h2 表示配置成功。
参见资料:Spring 配置 http2 文档:howto-configure-http2
Tomcat 版本差异文档:whichversion
4. 开启 http 访问端口(可选)。
由于此时只能通过 https 方式访问,可加入 Tomcat 配置开放新端口来支持 http 访问。
4.1 在启动类中增加 Bean。
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 配置文件中增加自定义配置。
http.port=18082
6. 重启 provider-demo,浏览器访问
http://127.0.0.1:18082/echo/1
。
此时 provider-demo 完成支持 https 和 http(通过不同端口访问),其中 https 访问时使用 http2 协议。
参见资料:Spring 配置 Tomcat 代码示例:SampleTomcatTwoConnectorsApplication.java。步骤3:改造 consumer-demo 工程
1. proxy 中的 @FeignClient 注解需指定 https 方式访问。
@FeignClient(name = "https://provider-demo")
2. RestTemplate 同理。
restTemplate.getForObject("https://provider-demo/echo/" + str, String.class);
3. 通过注入不同的 bean 选择是否使用SSL证书认证(以下步骤二选一):
1. 复制 jks 证书文件到 consumer-demo 工程的 resources 目录。
2. spring 配置文件中增加自定义配置。
test-ssl-config.key-store=classpath:keystore.jkstest-ssl-config.key-store-password: 123456
3. 修改 bean(restTemplate、feignClient)
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)
说明
与第一种方式的差异在 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,浏览器访问
http://127.0.0.1:18083/echo-feign/123
。
4.2 浏览器访问
http://127.0.0.1:18083/echo-rest/123
。