博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringCloud Gateway系列--https
阅读量:513 次
发布时间:2019-03-07

本文共 19946 字,大约阅读时间需要 66 分钟。

其他网址

简介

获取SSL证书

gateway与其余微服务通信

gateway在与微服务是通过http的,无论gateway配置的是http还是https,最终都会使用http与微服务通信。(zuul也是如此)。

比较老的gateway版本不支持将https转为http然后与微服务通信,解决方法见下方参考网址

公共代码

注册中心

application.yml

server:  port: 7001spring:  application:    name: eureka-servereureka:  instance:    hostname: localhost1  client:    register-with-eureka: false    fetch-registry: false    serviceUrl:      defaultZone: http://localhost:7001/eureka/# 下边是高可用配置#server:#  port: 7001##spring:#  application:#    name: server##eureka:#  instance:#    hostname: localhost1#  client:#    serviceUrl:#      defaultZone: http://localhost:7002/eureka/#server:#  port: 7002##spring:#  application:#    name: server##eureka:#  instance:#    hostname: localhost2#  client:#    serviceUrl:#      defaultZone: http://localhost:7001/eureka/

pom.xml

4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.12.RELEASE
com.example
eureka-server
0.0.1-SNAPSHOT
eureka-server
Demo project for Spring Boot
1.8
org.springframework.cloud
spring-cloud-starter-netflix-eureka-server
org.springframework.cloud
spring-cloud-dependencies
Greenwich.SR6
pom
import

product

application.yml

server:  port: 9002 #win10下9001端口被占用#server:#  port: 9003spring:  application:    name: producteureka:  client:    service-Url:      defaultZone: http://localhost:7001/eureka#      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eurekafeign:  hystrix:    enabled: true

controller

package com.example.product.controller;import lombok.extern.slf4j.Slf4j;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;@Slf4j@RestController@RequestMapping("/product")public class ProductController {    @GetMapping("/gateway")    public String feign1(){        return "succeed";    }}

gateway

将“简介”中获得的证书放到resources目录下。

pom.xml

4.0.0
org.springframework.boot
spring-boot-starter-parent
2.1.12.RELEASE
com.example
demo
0.0.1-SNAPSHOT
gateway
Demo project for Spring Boot
1.8
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-test
test
org.springframework.cloud
spring-cloud-starter-gateway
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.cloud
spring-cloud-dependencies
Greenwich.SR6
pom
import
org.springframework.boot
spring-boot-maven-plugin

接口请求

仅支持https

gateway微服务

application.yml

server:  port: 6443  ssl:    enabled: true    key-alias: tomcat    key-store: classpath:keystore.p12    key-store-password: 222333    keyStoreType: PKCS12spring:  application:    name: gateway  cloud:    gateway:      discovery:        locator:          enabled: trueeureka:  client:    service-url:      defaultZone: http://localhost:7001/eureka/# 配置Gateway日志等级,输出转发细节信息logging:  level:    org.springframework.cloud.gateway: debug

测试

访问:

同时支持https与http(https采用代码方式)

其他网址

  //由此配置方式获得启发

application.yml

# just http#server:#  port: 6001# just https#server:#  port: 6443#  ssl:#    enabled: true#    key-alias: tomcat#    key-store: classpath:keystore.p12#    key-store-password: 222333#    keyStoreType: PKCS12# http and httpsserver:  port: 6001# customhttps:  server:    port: 6443    ssl:      enabled: true      key-alias: tomcat      key-store: classpath:keystore.p12      key-store-password: 222333      keyStoreType: PKCS12spring:  application:    name: gateway  cloud:    gateway:      discovery:        locator:          enabled: true      httpclient:        ssl:          use-insecure-trust-manager: trueeureka:  client:    service-url:      defaultZone: http://localhost:7001/eureka/# 配置Gateway日志等级,输出转发细节信息logging:  level:    org.springframework.cloud.gateway: debug

配置类

简洁方式

配置类

package com.example.demo.config;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;import org.springframework.boot.web.server.WebServer;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.server.reactive.HttpHandler;@Configuration@ConditionalOnProperty(name = "https.server.ssl.enabled", havingValue = "true")public class HttpsConfig {    @Bean    @ConfigurationProperties(prefix = "https.server")    public HttpsProperties httpsProperties() {        return new HttpsProperties();    }    @Bean(initMethod = "start", destroyMethod = "stop")    public WebServer httpWebServer(HttpHandler handler, HttpsProperties properties) {        NettyReactiveWebServerFactory factory = new NettyReactiveWebServerFactory(properties.getPort());        factory.setSsl(properties.getSsl());        return factory.getWebServer(handler);    }}

属性类

package com.example.demo.config;import org.springframework.boot.web.server.Ssl;public class HttpsProperties {    private int port = 6443;    private Ssl ssl;    public int getPort() {        return port;    }    public void setPort(int port) {        this.port = port;    }    public Ssl getSsl() {        return ssl;    }    public void setSsl(Ssl ssl) {        this.ssl = ssl;    }}

复杂法 

package com.example.demo.config;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;import org.springframework.boot.web.server.Ssl;import org.springframework.boot.web.server.WebServer;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ClassPathResource;import org.springframework.http.server.reactive.HttpHandler;import java.io.File;import java.io.IOException;@Configuration@ConditionalOnProperty(name = "https.server.ssl.enabled", havingValue = "true")public class HttpsConfig {    @Value("${https.server.port:6443}")    private int httpsPort;    @Value("${https.server.ssl.key-alias:'tomcat'}")    private String keyAlias;    @Value("${https.server.ssl.key-store:'classpath:keystore.p12'}")    private String keyStore;    @Value("${https.server.ssl.key-store-password:'222333'}")    private String keyStorePassword;    @Value("${https.server.ssl.keyStoreType:'PKCS12'}")    private String keyStoreType;    @Bean(initMethod = "start", destroyMethod = "stop")    public WebServer httpWebServer(HttpHandler handler) {        NettyReactiveWebServerFactory factory = new NettyReactiveWebServerFactory(httpsPort);        Ssl ssl = new Ssl();        ssl.setEnabled(true);        ssl.setKeyAlias(keyAlias);        ssl.setKeyStore(keyStore);        ssl.setKeyStorePassword(keyStorePassword);        ssl.setKeyStoreType(keyStoreType);        factory.setSsl(ssl);        return factory.getWebServer(handler);    }}

踩坑记录 

下边这样写有问题。描述:在Idea下直接运行是可以的,但是打包成jar运行就会报错:

Caused by: java.io.FileNotFoundException: class path resource [keystore.p12] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/home/gateway-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/keystore.p12ClassPathResource

原因:打包成jar无法读取文件,要用流读取。

application.yml

# just http#server:#  port: 6001# just https#server:#  port: 6443#  ssl:#    enabled: true#    key-alias: tomcat#    key-store: classpath:keystore.p12#    key-store-password: 222333#    keyStoreType: PKCS12# http and httpsserver:  port: 6001# customhttps:  server:    port: 6443    ssl:      enabled: true      key-alias: tomcat      key-store: keystore.p12      key-store-password: 222333      keyStoreType: PKCS12spring:  application:    name: gateway  cloud:    gateway:      discovery:        locator:          enabled: true      httpclient:        ssl:          use-insecure-trust-manager: trueeureka:  client:    service-url:      defaultZone: http://localhost:7001/eureka/# 配置Gateway日志等级,输出转发细节信息logging:  level:    org.springframework.cloud.gateway: debug

配置类

package com.example.demo.config;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;import org.springframework.boot.web.server.Ssl;import org.springframework.boot.web.server.WebServer;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.core.io.ClassPathResource;import org.springframework.http.server.reactive.HttpHandler;import java.io.File;import java.io.IOException;@Configuration@ConditionalOnProperty(name = "https.server.ssl.enabled", havingValue = "true")public class HttpsConfig {    @Value("${https.server.port:6443}")    private int httpsPort;    @Value("${https.server.ssl.key-alias:'tomcat'}")    private String keyAlias;    @Value("${https.server.ssl.key-store:'classpath:keystore.p12'}")    private String keyStore;    @Value("${https.server.ssl.key-store-password:'222333'}")    private String keyStorePassword;    @Value("${https.server.ssl.keyStoreType:'PKCS12'}")    private String keyStoreType;    @Bean(initMethod = "start", destroyMethod = "stop")    public WebServer httpWebServer(HttpHandler handler) {        NettyReactiveWebServerFactory factory = new NettyReactiveWebServerFactory(httpsPort);        File keyStoreFile;        try {            keyStoreFile = new ClassPathResource(keyStore).getFile();        } catch (IOException ex) {            throw new IllegalStateException("can't access keystore: [" + "keystore"                    + "] or truststore: [" + "keystore" + "]", ex);        }        Ssl ssl = new Ssl();        ssl.setEnabled(true);        ssl.setKeyAlias(keyAlias);        ssl.setKeyStore(keyStoreFile.getAbsolutePath());        ssl.setKeyStorePassword(keyStorePassword);        ssl.setKeyStoreType(keyStoreType);        factory.setSsl(ssl);        return factory.getWebServer(handler);    }}

同时支持https与http(http采用代码方式)

其他网址

application.yml

server:  port: 6443  ssl:    enabled: true    key-alias: tomcat    key-store: classpath:keystore.p12    key-store-password: 222333    keyStoreType: PKCS12# customhttp:  server:    enabled: true    port: 6001spring:  application:    name: gateway  cloud:    gateway:      discovery:        locator:          enabled: trueeureka:  client:    service-url:      defaultZone: http://localhost:7001/eureka/# 配置Gateway日志等级,输出转发细节信息logging:  level:    org.springframework.cloud.gateway: debug

配置类

法1:创建http服务器

高级写法

package com.landsky.ener.gateway.config;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;import org.springframework.boot.web.server.WebServer;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.server.reactive.HttpHandler;@Configuration@ConditionalOnProperty(name = "http.server.enabled", havingValue = "true")public class HttpConfiguration {    @Value("${http.server.port:6001}")    private int httpPort;    @Bean(initMethod = "start", destroyMethod = "stop")    public WebServer httpWebServer(HttpHandler handler) {        NettyReactiveWebServerFactory factory = new NettyReactiveWebServerFactory(httpPort);        return factory.getWebServer(handler);    }}

低级写法

package com.example.demo.config;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;import org.springframework.boot.web.server.WebServer;import org.springframework.context.annotation.Configuration;import org.springframework.http.server.reactive.HttpHandler;import javax.annotation.PostConstruct;import javax.annotation.PreDestroy;@Configurationpublic class HttpConfig {    @Value("${http.server.port:0}")    private int httpPort;    @Value("${http.server.enabled:0}")    private Boolean httpEnabled;    @Autowired    private HttpHandler httpHandler;    private WebServer webServer;    @PostConstruct    public void start() {        if (httpEnabled) {            NettyReactiveWebServerFactory factory = new NettyReactiveWebServerFactory(httpPort);            webServer = factory.getWebServer(httpHandler);            webServer.start();        }    }    @PreDestroy    public void stop() {        webServer.stop();    }}

法2:http转https

package com.example.demo.config;import org.springframework.beans.factory.annotation.Value;import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;import org.springframework.context.annotation.Configuration;import org.springframework.http.HttpStatus;import reactor.core.publisher.Mono;import javax.annotation.PostConstruct;import java.net.URI;import java.net.URISyntaxException;@Configurationpublic class HttpToHttpsRedirectConfig {    @Value("${http.server.port:0}")    private int httpPort;    @Value("${http.server.enabled:0}")    private Boolean httpEnabled;    @Value("${server.port:0}")    private int httpsPort;    @Value("${server.ssl.enabled:0}")    private Boolean httpsEnabled;    @PostConstruct    public void startRedirectServer() {        if (httpEnabled) {            if (httpsEnabled) {                NettyReactiveWebServerFactory httpNettyReactiveWebServerFactory = new NettyReactiveWebServerFactory(httpPort);                httpNettyReactiveWebServerFactory.getWebServer((request, response) -> {                    URI uri = request.getURI();                    URI httpsUri;                    try {                        httpsUri = new URI("https", uri.getUserInfo(), uri.getHost(), httpsPort, uri.getPath(), uri.getQuery(), uri.getFragment());                    } catch (URISyntaxException e) {                        return Mono.error(e);                    }                    response.setStatusCode(HttpStatus.MOVED_PERMANENTLY);                    response.getHeaders().setLocation(httpsUri);                    return response.setComplete();                }).start();            }        }    }}

测试

访问http:

访问https:

http请求转为https请求(待解决)

Websocket请求

其他网址

简介

配置支持https后,就会支持wss(websocket https)。

本处修改“公共代码”进行测试。(本处直接支持ws与wss(与支持http与https配置是一样的))

product

websockt工具类

package com.example.product.util; import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;import org.springframework.util.StringUtils;import javax.websocket.*;import javax.websocket.server.PathParam;import javax.websocket.server.ServerEndpoint;import java.io.IOException;import java.util.Map;import java.util.concurrent.ConcurrentHashMap; @Slf4j@Component@ServerEndpoint(value = "/ws/{token}")public class WebSocketServer {    //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。    private static int onlineCount = 0;     //保存客户端所对应的WebSocketServer    private static Map
clientMap = new ConcurrentHashMap<>(); private String token; //与某个客户端的连接会话,需要通过它来给客户端发送数据 private Session session; // 连接建立成功调用的方法 @OnOpen public void onOpen(@PathParam("token") String token, Session session) { //TODO 校验token this.session = session; addOnlineCount(); clientMap.put(token, this); log.info("新连接加入!" + " token:" + token + "; session.getId():" + session.getId() + " 当前连接数:" + onlineCount); } // 连接关闭 @OnClose public void onClose() { subOnlineCount(); clientMap.remove(token); log.info("有一连接关闭,当前连接数为:" + onlineCount); } // 收到客户端消息 @OnMessage public void onMessage(String message, Session session) throws IOException { log.info("来自客户端的消息:" + message); sendMsgToAll(message); } // 发生错误 @OnError public void onError(Session session, Throwable error) { log.info("发生错误!"); error.printStackTrace(); } public void sendMessage(String token, String message) throws IOException { if (!StringUtils.isEmpty(token) && clientMap.containsKey(token)) { clientMap.get(token).send(message); log.info("成功发送一条消息:" + message); } else { log.error("用户:" + token + ",不在线!"); } } public void send(String message) throws IOException{ this.session.getBasicRemote().sendText(message); } // 给所有客户端群发消息 public void sendMsgToAll(String message) throws IOException { for (WebSocketServer item : clientMap.values()) { item.session.getBasicRemote().sendText(message); } log.info("成功群发一条消息:" + onlineCount); } public static synchronized int getOnlineCount() { return WebSocketServer.onlineCount; } public static synchronized void addOnlineCount() { WebSocketServer.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketServer.onlineCount--; }}

websocket配置类

package com.example.product.config; import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configurationpublic class WebSocketConfig {    @Bean    public ServerEndpointExporter serverEndpointExporter() {        return new ServerEndpointExporter();    }}

测试

用此在线网址测试:

测试ws

连接到:ws://127.0.0.1:6001/PRODUCT/ws/1

测试wss

 连接到:wss://127.0.0.1:6443/PRODUCT/ws/1

连接失败!

后台打印:

io.netty.handler.codec.DecoderException: javax.net.ssl.SSLException: Received fatal alert: certificate_unknown

打开F12,重新连一下

解决方法

1. 打开 Chrome,新开一个Tab页面。

2. 访问自己的测试域名(wss替换为https):https://127.0.0.1:6443/PRODUCT/ws/1。
3. 浏览器告警:"您的连接不是私密连接......."。
4. 点"高级",继续点击 "继续前往 www.wss.com(不安全)"。
5. 页面提示"400 Bad Request......"。不用理会,这是因为用HTTP协议访问WSS服务。
此时重新连接wss://127.0.0.1:6443/PRODUCT/ws/1

疑问及解答 

问:对于自己颁发的证书,为了使wss连接成功,每一个wss都要这样改为https然后访问一下吗?

答:不是的。只要访问了其对应的https、域名(ip)、端口的一个网址,同一https/wss+域名(ip)+端口和的网址/websocket连接全都可以正常了。比如:登录时用的是https,之后所有同一https/wss+域名(ip)+端口的网址/websocket连接就全部可以用了,而项目里一般websocket的域名(ip)+端口与接口(比如登录)是一样的,所以所有wss都正常连接了。

 

 

转载地址:http://wntjz.baihongyu.com/

你可能感兴趣的文章