前置条件:可用的SSL证书
需要两个文件,key格式的还有pem格式的
如果是阿里云证书,下面Nginx类型的就可以
快捷转移——数字证书管理服务管理控制台 (aliyun.com)
自建证书
以管理员形式打开cmd窗口,如果提示keytool不存在,将目录定位到java安装目录的bin下方。
1、生成证书
keytool -genkey -alias myalias -keyalg RSA -keysize 2048 -keystore mykeystore.jks -validity 365
2、jks证书转化为pem格式
keytool -export -alias myalias -keystore mykeystore.jks -rfc -file certificate.pem
3、导出私钥
keytool -importkeystore -srckeystore mykeystore.jks -destkeystore temp.p12 -srcalias myalias -deststoretype PKCS12 -srcstorepass <keystore_password> -deststorepass <p12_password>
4、提取私钥
openssl pkcs12 -in temp.p12 -nocerts -nodes -out private_key.pem
这种方式得到两个接下来要用到的文件
① certificate.pem
② private_key.pem
阿里云证书的方式直接下载得到的是key和pem,需要将key转化为私钥
执行openssl rsa -in old_server_key.pem -out private_key.pem
这样也得到了上述两个文件
接下来使用netty搭建简易websocket服务器
@Configuration
@Slf4j
public class NettyWebSocketServer {private final static int WEB_SOCKET_PORT = 9090;private final EventLoopGroup bossGroup = new NioEventLoopGroup(1);private final EventLoopGroup workerGroup = new NioEventLoopGroup(NettyRuntime.availableProcessors());@PostConstructpublic void start() throws Exception {run();}@PreDestroypublic void destroy() {Future<?> future = bossGroup.shutdownGracefully();Future<?> future1 = workerGroup.shutdownGracefully();future.syncUninterruptibly();future1.syncUninterruptibly();log.info("关闭 ws server 成功");}public void run() throws Exception {ServerBootstrap serverBootstrap = new ServerBootstrap();SslContext sslContext = SslContextBuilder.forServer(getCertPem(), getCertKey()).build();serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 128).option(ChannelOption.SO_KEEPALIVE, true).handler(new LoggingHandler(LogLevel.INFO)) // 为 bossGroup 添加 日志处理器.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {ChannelPipeline pipeline = socketChannel.pipeline();// 注意将SSL必须是第一个处理器pipeline.addLast(sslContext.newHandler(socketChannel.alloc()));//30秒客户端没有向服务器发送心跳则关闭连接pipeline.addLast(new IdleStateHandler(30, 0, 0));// 因为使用http协议,所以需要使用http的编码器,解码器pipeline.addLast(new HttpServerCodec());// 以块方式写,添加 chunkedWriter 处理器pipeline.addLast(new ChunkedWriteHandler());/*** 说明:* 1. http数据在传输过程中是分段的,HttpObjectAggregator可以把多个段聚合起来;* 2. 这就是为什么当浏览器发送大量数据时,就会发出多次 http请求的原因*/pipeline.addLast(new HttpObjectAggregator(8192));//保存用户ippipeline.addLast(new HttpHeadersHandler());/*** */pipeline.addLast(new WebSocketServerProtocolHandler("/"));}});// 启动服务器,监听端口,阻塞直到启动成功serverBootstrap.bind(WEB_SOCKET_PORT).sync();log.info("netty在{}启动成功", WEB_SOCKET_PORT);}private InputStream getCertPem() {ClassPathResource classPathResource = new ClassPathResource("certificate.pem");return classPathResource.getStream();}private InputStream getCertKey() {ClassPathResource classPathResource = new ClassPathResource("private_key.pem");return classPathResource.getStream();}}
然后加成jar包放到服务器上启动。
配置nginx反向代理
server{listen 443 ssl;server_name your-server-name; # 填写①ssl_certificate certificate.pem; # 填写路径②ssl_certificate_key private_key.pem; # 填好路径③ssl_session_timeout 5m;root /www/wwwroot; # 自行配置charset utf-8;location /netty{proxy_pass https://your-address-url:9090/; # 填写④ 不能是9090 必须是9090/proxy_http_version 1.1;proxy_set_header Upgrade $http_upgrade;proxy_set_header Connection "Upgrade";rewrite "^/netty/(.*)$" /$1 break;}
}
测试 wss://your-server-name/netty 可以连接成功
常见错误
1、io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record
错误说明没有使用SSL/TLS
导致的原因可能是:
① 使用了ws协议连接
② nginx转发的时候使用了http,需要使用https
2、wss Received fatal alert: certificate_unknown
错误说明证书未知
导致的原因可能是:
① 证书错误、不被信任