有奖捉虫:办公协同&微信生态&物联网文档专题 HOT

操作场景

本文以 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=true
server.ssl.key-store=classpath:keystore.jks
server.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微服务调用时请启用
@EnableTsf
public class ProviderApplication {

@Value("${http.port}")
private Integer port;

/**
* Tomcat增加支持http访问
**/
@Bean
public 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证书认证(以下步骤二选一):
使用 SSL 证书认证访问方式
忽略 SSL 认证方式
1. 复制 jks 证书文件到 consumer-demo 工程的 resources 目录。
2. spring 配置文件中增加自定义配置。


test-ssl-config.key-store=classpath:keystore.jks
test-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微服务调用时请启用
@EnableTsf
public class ConsumerApplication {

@Value("${test-ssl-config.key-store}")
private String file;
@Value("${test-ssl-config.key-store-password}")
private String password;

@LoadBalanced
@Bean
public 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);
}

@Bean
public 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
@Bean
public AsyncRestTemplate asyncRestTemplate() {
return new AsyncRestTemplate();
}

public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
只需要修改 bean(restTemplate、feignClient)






说明
与第一种方式的差异在 getSSLSocket() 方法。
```java
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.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微服务调用时请启用
@EnableTsf
public class ConsumerApplication {

@LoadBalanced
@Bean
public 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);
}

@Bean
public 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() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) {}

@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) {}

@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[]{tm}, null);
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
return sslContext;
}

@LoadBalanced
@Bean
public 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