SpringCloud Gateway--支持https

原文网址:SpringCloud Gateway--支持https_IT利刃出鞘的博客-CSDN博客

简介

说明

        本文介绍SpringCloud Gateway如何支持https。

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

官网

7. TLS / SSL (spring cloud gateway官网)

获取SSL证书

后台项目应用系列--https_feiying0canglang的博客-CSDN博客

gateway与其余微服务通信

公共代码

注册中心

application.yml

server:
  port: 7001

spring:
  application:
    name: eureka-server

eureka:
  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: 9003

spring:
  application:
    name: product

eureka:
  client:
    service-Url:
      defaultZone: http://localhost:7001/eureka
#      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka

feign:
  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: PKCS12

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true

eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/

# 配置Gateway日志等级,输出转发细节信息
logging:
  level:
    org.springframework.cloud.gateway: debug

测试

访问:https://localhost:6443/PRODUCT/product/gateway

SpringCloud Gateway--支持https_第1张图片

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

其他网址

后台项目应用系列--https_feiying0canglang的博客-CSDN博客  //由此配置方式获得启发

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 https
server:
  port: 6001

# custom
https:
  server:
    port: 6443
    ssl:
      enabled: true
      key-alias: tomcat
      key-store: classpath:keystore.p12
      key-store-password: 222333
      keyStoreType: PKCS12

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      httpclient:
        ssl:
          use-insecure-trust-manager: true

eureka:
  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 https
server:
  port: 6001

# custom
https:
  server:
    port: 6443
    ssl:
      enabled: true
      key-alias: tomcat
      key-store: keystore.p12
      key-store-password: 222333
      keyStoreType: PKCS12

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      httpclient:
        ssl:
          use-insecure-trust-manager: true

eureka:
  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采用代码方式)

其他网址

SpringCloud Gateway网关同时支持http和https访问_u013998466的博客-CSDN博客

Spring Cloud Gateway同时监听HTTP和HTTPS(http自动转发https端口)_jingle_1995的博客-CSDN博客
Spring cloud gateway 设置https 和http同时支持_荡漾-CSDN博客
SpringCloud Gateway网关同时支持http和https访问_u013998466的博客-CSDN博客

官网

application.yml

server:
  port: 6443
  ssl:
    enabled: true
    key-alias: tomcat
    key-store: classpath:keystore.p12
    key-store-password: 222333
    keyStoreType: PKCS12
# custom
http:
  server:
    enabled: true
    port: 6001

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true

eureka:
  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;

@Configuration
public 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;

@Configuration
public 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:http://localhost:6001/PRODUCT/product/gateway

SpringCloud Gateway--支持https_第2张图片

访问https:https://localhost:6443/PRODUCT/product/gateway

SpringCloud Gateway--支持https_第3张图片

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

Websocket请求

其他网址

【原创】解决WSS报错:WebSocket connection failed: Error in connection establishment: net::ERR_CERT_AUTHORITY_INVALID - 金牛座, 爬山虎

简介

配置支持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;
 
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

测试

用此在线网址测试:websocket/ws/wss在线调试测试工具

测试ws

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

SpringCloud Gateway--支持https_第4张图片

测试wss

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

SpringCloud Gateway--支持https_第5张图片

连接失败!

后台打印:

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

打开F12,重新连一下

SpringCloud Gateway--支持https_第6张图片

解决方法

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

SpringCloud Gateway--支持https_第7张图片

疑问及解答 

问:对于自己颁发的证书,为了使wss连接成功,每一个wss都要这样改为https然后访问一下吗?
答:不是的。只要访问了其对应的https、域名(ip)、端口的一个网址,同一https/wss+域名(ip)+端口和的网址/websocket连接全都可以正常了。比如:登录时用的是https,之后所有同一https/wss+域名(ip)+端口的网址/websocket连接就全部可以用了,而项目里一般websocket的域名(ip)+端口与接口(比如登录)是一样的,所以所有wss都正常连接了。

其他网址

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

SpringCloud Gateway Https设置 以Http 转发 路由 给后台微服务_jingle_1995的博客-CSDN博客
spring-cloud-gateway使用https注意事项2---如何在转发后端服务的时候使用http - 简书

你可能感兴趣的:(SpringCloud,https,spring,boot,spring)