HTTPS与WSS证书配置

通过服务端部署HTTPS与WSS证书配置

一、Spring Cloud Gateway配置HTTPS

1、阿里云申请免费证书

登陆之后,直接在产品和服务器中搜索【证书】
001.png

2、购买证书

002.png

3、配置域名

003.png

4、下载证书

这里下载的是tomcat证书

005.png

上面txt文件内容是密码

5、配置网关ssl

server:
  port: 9527
  #以下配置ssl证书
  ssl:
    key-store: classpath:7225224_xfjs.fun.pfx #证书文件路径
    key-store-type: PKCS12  #证书类型
    enabled: true
    key-store-password: *******11123 #证书验证密码
  • 把证书复制到resources目录下
  • bootstrap.yml配置ssl,如上配置

6、如果出现如下错误

005.png

  • 则要在pom.xml添加一个插件:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-resources-plugin</artifactId>
    <configuration>
        <encoding>UTF-8</encoding>
        <!-- 过滤后缀为pkcs12、jks的证书文件 -->
        <nonFilteredFileExtensions>
            <nonFilteredFileExtension>pfx</nonFilteredFileExtension>
            <nonFilteredFileExtension>jks</nonFilteredFileExtension>
        </nonFilteredFileExtensions>
    </configuration>
</plugin>

7、此时访问网关会报如下错误

io.netty.handler.codec.DecoderException: io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record: 485454502f312e3120343030200d0a5472616e736665722d456e636f64696e673a206368756e6b65640d0a446174653a205468752c2031372053657020323032302030383a32333a323320474d540d0a436f6e6e656374696f6e3a20636c6f73650d0a0d0a300d0a0d0a
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:459)
	at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:265)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:628)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:563)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:480)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:442)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:884)
	at java.lang.Thread.run(Thread.java:748)
Caused by: io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record: 485454502f312e3120343030200d0a5472616e736665722d456e636f64696e673a206368756e6b65640d0a446174653a205468752c2031372053657020323032302030383a32333a323320474d540d0a436f6e6e656374696f6e3a20636c6f73650d0a0d0a300d0a0d0a
	at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1178)
	at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1243)
	at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:489)
	at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:428)
	... 15 common frames omitted
  • 是因为用户使用https访问网关,网关也会使用https访问各个微服务,解决方法是在bootstrap.yml配置把https转成http:
#这是资料的原版写法------------------------------
spring:
  cloud:
    gateway:
      routes: 
      - id: router1
        predicates:
        - Host=sample.com
        - Path=/**
        uri: lb:http://sample-web


#这是我在项目中的实际写法-----------------------------------------------
spring:
  application:
    name: gateway-server
  cloud:
    nacos:
      server-addr: 000.000.000.000:00#换成自己的服务地址
    gateway:
      discovery:
        locator:
          enabled: true #开启服务发现
          lower-case-service-id: true #启用小驼峰的服务名称访问服务
      routes:
        - id: xf-admin_router
          #uri: lb://xf-admin #转发到哪个目的地
          #下面哪个为什么要加上 http: 呢,因为使用ssl证书之后,网关转发的也是https请求,但是调用的微服务需要的是http请求,所以这样
          uri: lb:http://xf-admin #转发到哪个目的地
          predicates:
             - Path=/admin/**
  • lb:[overwite scheme]😕/sample-web 这行加上http就行了,原理查看LoadBalancerClientFilter.class源码

8、配置tomcat(如果是使用jar包部署则不需要这一步)

  • 解压已下载保存到本地的Tomcat证书文件。
  • 解压后您将看到文件夹中有2个文件,您可为两个证书文件重命名。
  • 证书文件(domain name.pfx):以.pfx为后缀或文件类型。
    密码文件(pfx-password.txt):以.txt为后缀或文件类型。
  • 在Tomcat安装目录下新建cert目录,将解压的证书和密码文件拷贝到cert目录下。
  • 修改配置文件server.xml,并保存。
  • 文件路径:Tomcat安装目录/conf/server.xml
  • 在标签内添加:
<Connector port="443"  
    protocol="HTTP/1.1"
    SSLEnabled="true"
    scheme="https"
    secure="true"
    keystoreFile="D:/web-server/apache-tomcat-common/cert/domain.pfx" 
    keystoreType="PKCS12"
    keystorePass="password"
    clientAuth="false"
    SSLProtocol="TLSv1+TLSv1.1+TLSv1.2"
    ciphers="TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256"/>

可选: 配置web.xml文件,开启HTTP强制跳转HTTPS。在文件后添加以下内容:

<login-config>  
    <!-- Authorization setting for SSL -->  
    <auth-method>CLIENT-CERT</auth-method>  
    <realm-name>Client Cert Users-only Area</realm-name>  
</login-config>  
<security-constraint>  
    <!-- Authorization setting for SSL -->  
    <web-resource-collection >  
        <web-resource-name >SSL</web-resource-name>  
        <url-pattern>/*</url-pattern>  
    </web-resource-collection>  
    <user-data-constraint>  
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>  
    </user-data-constraint>  
</security-constraint>

二、Gateway同时支持https和http请求

这个配置简单,但是我们需要同时支持http 和https

目前这个问题官方还没有正式解决

民间人士,想到的办法是 在启动一个Netty 监听另外一个端口 如:8080,这个端口是不带 s 的也就是 http

当访问8080时 跳转到配置https 的如8443端口

经过一番查找

  • https://github.com/spring-projects/spring-boot/issues/12035

  • https://stackoverflow.com/questions/49045670/spring-webflux-redirect-http-to-https/53000573#53000573

具体实现

package cn.com.test.gateway.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;
 
/**
 * 该类主要是将http请求转化为https请求
 * @author shkstart
 * @create 2022-02-17 21:47
 */
@Configuration
public class HttpToHttpsRedirectConfig {
 
    @Value("${server.http.port}")
    private int httpPort;
    @Value("${server.port}")
    private int serverPort;
 
    @PostConstruct
    public void startRedirectServer() {
        NettyReactiveWebServerFactory httpNettyReactiveWebServerFactory = new NettyReactiveWebServerFactory(httpPort);
        httpNettyReactiveWebServerFactory.getWebServer((request, response) -> {
            URI uri = request.getURI();
            URI httpsUri;
            try {
                httpsUri = new URI("https", uri.getUserInfo(), uri.getHost(), serverPort, 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();
    }
 
 
}

yml 配置文件配置:

#分为俩个端口号,server.port 是配置https 的端口, http.port 是监听的端口
    server:
      http:
        port: 4321
      port: 1234
      ssl:
        enabled: true
        key-alias: scg
        key-store: classpath:ssl/test.pfx
        key-store-password: ******sad #换成自己的
        keyStoreType: PKCS12

  • 启动后访问,不带s 的4321端口会转发到1234端口。

三、Netty配置证书,实现wss访问

1、创建证书

自制证书:

keytool -genkey -keysize 2048 -validity 365 -keyalg RSA -dnam e "CN=gornix.com" -keypass 654321 -storepass 123456 -keystore gornix.jks

keytool为JDK提供的生成证书工具

  • -keysize 2048 密钥长度2048位(这个长度的密钥目前可认为无法被暴力破解)
  • -validity 365 证书有效期365天
  • -keyalg RSA 使用RSA非对称加密算法
  • -dname “CN=gornix.com” 设置Common Name为gornix.com,这是我的域名
  • -keypass 654321 密钥的访问密码为654321
  • -storepass 123456 密钥库的访问密码为123456(其实这两个密码也可以设置一样,通常都设置一样,方便记)
  • -keystore gornix.jks 指定生成的密钥库文件为gornix.jks

2、保存证书文件(我这里直接放在resource目录下)

把生成的证书放到服务器上(也可以放在本地,不过这样要在本地代码配置ssl证书,使用https才可以,理论如此,因为我本地没有配ssl所以我直接部署在服务器上使用的),我放在/home/new 包下

3、jks证书处理工具类

package com.xf.chat.websocket;

import org.springframework.util.ResourceUtils;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;

/**
 * 这个类主要是处理 wss证书
 * @author shkstart
 * @create 2022-02-10 22:41
 */
public class SslUtil {
    private static volatile SSLContext SSL_CONTENT = null;
    public static SSLContext createSSLContext(String type, String path, String password) throws Exception {
        if (null == SSL_CONTENT) {
            synchronized (SslUtil.class) {
                if (null == SSL_CONTENT) {
                    //type应该是 JKS
                    KeyStore ks = KeyStore.getInstance(type); /// "JKS"    

                    //读取resource下的文件
                    //File file = ResourceUtils.getFile("classpath:excleTemplate/test.xlsx");
                    File file = ResourceUtils.getFile(path);
                    InputStream ksInputStream = new FileInputStream(file);
                    //读取磁盘文件
                    //InputStream ksInputStream = new FileInputStream(path); /// 证书存放地址
                    ks.load(ksInputStream, password.toCharArray());
                    //KeyManagerFactory充当基于密钥内容源的密钥管理器的工厂。
                    KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());//getDefaultAlgorithm:获取默认的 KeyManagerFactory 算法名称。
                    kmf.init(ks, password.toCharArray());
                    //SSLContext的实例表示安全套接字协议的实现,它充当用于安全套接字工厂或 SSLEngine 的工厂。
                    SSLContext sslContext = SSLContext.getInstance("TLS");
                    sslContext.init(kmf.getKeyManagers(), null, null);
                    SSL_CONTENT =sslContext;
                }
            }
        }
        return SSL_CONTENT;
    }
}

4、初始化netty时加载证书类

package com.xf.chat.websocket;

import com.xf.chat.service.ChatMsgService;
import com.xf.chat.websocket.handler.ChatHandler;
import com.xf.chat.websocket.handler.QuitHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.springframework.beans.factory.annotation.Autowired;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;

/**
 * @author shkstart
 * @create 2021-12-04 23:37
 */
public class WSServerInitialzer extends ChannelInitializer<NioSocketChannel> {

    /**
     * 初始化方法
     *
     * @param channel
     * @throws Exception
     */
    @Override
    protected void initChannel(NioSocketChannel channel) throws Exception {
        //获取管道
        ChannelPipeline pipeline = channel.pipeline();

        /**
         * 这里配置自制的wss证书
         * type: 这里用JKS
         * path: 证书文件地址
         * password: 证书密码
         */
        String path = "classpath:gornix.jks";
        String password = "123456";
        SSLContext sslContext = SslUtil.createSSLContext("JKS", path, password);
        SSLEngine sslEngine = sslContext.createSSLEngine();
        // 是否使用客户端模式
         // sslEngine.setNeedClientAuth(false);
        //是否需要验证客户端
        sslEngine.setUseClientMode(false); //服务器端模式
        pipeline.addLast("ssl", new SslHandler(sslEngine));
        //------------------------------------------------------------

        //加入webSocket 基于http协议的编解码器
        pipeline.addLast(new HttpServerCodec());
        //对数据流写 提供支持
        pipeline.addLast(new ChunkedWriteHandler());
        //对httpMessage进行聚合处理,聚合成request或response
        pipeline.addLast(new HttpObjectAggregator(1024 * 64));
        /**
         * 这个handler 可以帮住处理
         * 握手动作:handshaking(close、ping、pong) 即心跳
         * 对于webSocket来讲,都是以frams进行传输的,不同的数据类型独赢的frams也不同
         */
        //访问路径这里得wss是后边得后缀wss,至于前边得wss是签名配置过wss证书 "wss://127.0.0.1:8000/wss",
        pipeline.addLast(new WebSocketServerProtocolHandler("/wss"));

        /**
         * 自定义handle
         */
        pipeline.addLast(new ChatHandler());

        //处理连接断开的handler
        pipeline.addLast(new QuitHandler());

    }
}

使用nginx反向代理部署https和wss(推荐)

(必读)

通过这种方法部署之后可能还需要在gateway上部署一边证书,不需要部署netty证书,详细请参看一条

1、阿里云申请免费证书

登陆之后,直接在产品和服务器中搜索【证书】

在这里插入图片描述

2、购买证书

在这里插入图片描述

3、配置域名

在这里插入图片描述

4、下载证书

这里下载的是naginx证书

006.jpg

5、将下载下来的证书解压后的两个文件上传至服务器

  • 在 /usr/local/nginx/conf下创建 cert文件夹用于存放证书文件

007.jpg

  • 将解压缩后的两个证书文件上传cert文件夹下

008.jpg

6、修改conf下的nginx.conf配置文件

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;




 
   	#如果$http_upgrade 不为 '' (空), 则$connection_upgrade 为 upgrade 。
    #如果$http_upgrade 为 '' (空), 则 $connection_upgrade 为 close。
 	 map $http_upgrade $connection_upgrade { 
		 default upgrade; 
 		 '' close; 
	 } 

    

	#表示的是 nginx负载均衡:
	#两台服务器 (ip1:port1)和(ip2:port2) 。
	#keepalive 1000 表示的是每个nginx进程中上游服务器保持的空闲连接,当空闲连接过多时,会关闭最少使用的空闲连接.当然,这不是限制连接总数的,可以想象成空闲连接池的大小,设置的值应该是上游服务器能够承受的。
    upstream wsbackend {
	 server 00.00.000.000:8000 #换成自己的;
	 #server ip1:port1; 
 	 #server ip2:port2; 
	 #keepalive 1000; 
    }




#以下属性中,以ssl开头的属性表示与证书配置有关。
server {
    listen 443 ssl;
    #配置HTTPS的默认访问端口为443。
    #如果未在此处配置HTTPS的默认访问端口,可能会造成Nginx无法启动。
    #如果您使用Nginx 1.15.0及以上版本,请使用listen 443 ssl代替listen 443和ssl on。
    server_name xfjs.fun; #需要将yourdomain替换成证书绑定的域名。
    root html;
    index index.html index.htm;
    ssl_certificate cert/7225224_xfjs.fun.pem;  #需要将cert-file-name.pem替换成已上传的证书文件的名称。
    ssl_certificate_key cert/7225224_xfjs.fun.key; #需要将cert-file-name.key替换成已上传的证书私钥文件的名称。
    ssl_session_timeout 5m;
    ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
    #表示使用的加密套件的类型。
    ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; #表示使用的TLS协议的类型。
    ssl_prefer_server_ciphers on;

	#这里配置的是wss相关配置
    #listen 20038 表示 nginx 监听的端口
    #locations / 表示监听的路径(/表示所有路径,通用匹配,相当于default)
    #proxt_http_version 1.1 表示反向代理发送的HTTP协议的版本是1.1,HTTP1.1支持长连接
    #proxy_pass http://wsbackend; 表示反向代理的uri,这里可以使用负载均衡变量
    #proxy_redirect off; 表示不要替换路径,其实这里如果是/则有没有都没关系,因为default也是将路径替换到proxy_pass的后边
    #proxy_set_header Host $host; 表示传递时请求头不变, $host是nginx内置变量,表示的是当前的请求头,proxy_set_header表示设置请求头
    #proxy_set_header X-Real-IP $remote_addr; 表示传递时来源的ip还是现在的客户端的ip
    #proxy_read_timeout 3600s; 表的两次请求之间的间隔超过 3600s 后才关闭这个连接,默认的60s,自动关闭的元凶
    #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 表示X-Forwarded-For头不发生改变
    #proxy_set_header Upgrade $http_upgrade; 表示设置Upgrade不变
    #proxy_set_header Connection $connection_upgrade; 表示如果 $http_upgrade为upgrade,则请求为upgrade(websocket),如果不是,就关闭连接
	#此时,访问 wss://域名:443/wss 就会被转发到 ip1:port1 和 ip2:port2 上。
   location /wss {
   		proxy_http_version 1.1;
  		 proxy_pass http://wsbackend;
  		 proxy_redirect off; 
  		 proxy_set_header Host $host; 
  		 proxy_set_header X-Real-IP $remote_addr; 
  		 proxy_read_timeout 3600s; 
  		 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
  		 proxy_set_header Upgrade $http_upgrade; 
  		 proxy_set_header Connection $connection_upgrade; 
	 }
    
    #以下配置的就是通过https访问443端口之后要跳转的网站首页
     location / {
            root   /app/fighting-admin/dist;
            index  index.html index.htm;
	        try_files $uri $uri/  @router;
	        index index.html;
        }
     location @router {
			rewrite ^.*$  /index.html last;
		}


}

#这里的配置是将所有的80端口的请求重定向到https请求
server {
    listen 80;
    server_name xfjs.fun; #需要将yourdomain替换成证书绑定的域名。
    rewrite ^(.*)$ https://$host$1; #将所有HTTP请求通过rewrite指令重定向到HTTPS。
    location / {
        index index.html index.htm;
    }
}

  
}

  • 36
    点赞
  • 57
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值