本文共 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
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"; }}
将“简介”中获得的证书放到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
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
测试
访问:
其他网址
//由此配置方式获得启发
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); }}
其他网址
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:
其他网址
简介
配置支持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 MapclientMap = 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/