前置条件:可用的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());
@PostConstruct
public void start() throws Exception {
run();
}
@PreDestroy
public 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>() {
@Override
protected 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));
//保存用户ip
pipeline.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
错误说明证书未知
导致的原因可能是:
① 证书错误、不被信任
评论前必须登录!
注册