Netty/websocket server configuration Alibaba cloud SSL certificate security access configuration, pro-test effective

Background: Java microservices include https access and websocket access. When the https interface accesses ws requests, an error is reported, because https can access wss.

After applying for a free certificate from Alibaba Cloud, search for various tutorials such as nginx configuration methods, netty access certificates, etc. After a lot of detours, I finally got through one.

Key points: 1. Because netty is used, the way nginx configures wss does not work. You need to put the certificate in the way of starting netty to start it.

2. Most of the online tutorials are pkcs12 certificate generation methods. But netty only supports the pkcs8 version, so you need to generate pkcs12 and then transfer to pkcs8.

1. Alibaba Cloud applies for a free certificate

 2. Generate pem and key certificates

1. Download the pem certificate of type Nginx

2. Download the pfx certificate of the Tomcat type, generate the server.key file with the cmd command under the jdk path (or directly in the folder), enter the command, and the password is in the pfx-password.txt together

openssl pkcs12 -in *******.pfx -nocerts -nodes -out server.key

 

 

3. Because netty only supports pkcs8, so the server.key will generate the pkcs8 version through OpenSSl

install openssl

Windows computer downloads the compiled openssl
OpenSSL for Windows

or

OpenSSL official download - code off
Then add the installation path to the environment variable

The cmd command generates server8.key:

openssl pkcs8 -topk8 -inform PEM -in server.key -outform pem -nocrypt -out server8.key

 

 

3. Write code

Just need to add two places:

1. The start method of startup increases the reading of server8.key and pem files

global variable

private SslContext sslContext;//netty 配置ssl证书、WSS地址访问。备注:netty 仅仅使用pkcs8的证书,需要使用openssl转

 Put the certificate under main/resources, then read it with ClassPathResource

//ssl证书配置--放到项目中
ClassPathResource pem = new ClassPathResource("service.sv3d.cn.pem");
ClassPathResource key = new ClassPathResource("server8.key");
this.sslContext = SslContextBuilder.forServer(pem.getInputStream(),key.getInputStream()).build();

 Put the certificate outside to facilitate later replacement. After all, the free certificate is applied for once a year, read with InputStream

//ssl证书配置--放到外部
InputStream pemInputStream = new FileInputStream("E:\\zhengshu\\9760642_service.sv3d.cn_nginx\\9760642_service.sv3d.cn.pem"); /// 证书存放地址
InputStream keyInputStream = new FileInputStream("E:\\zhengshu\\9760642_service.sv3d.cn_tomcat\\server8.key"); /// 证书存放地址
this.sslContext = SslContextBuilder.forServer(pemInputStream,keyInputStream).build();

2. The initChannel method adds ssl verification

ch.pipeline().addLast(sslContext.newHandler(ch.alloc()));

3. For all the codes, just look at the modified part, which is similar.

    /**
     * 启动
     * @throws InterruptedException
     */
    private void start() throws InterruptedException {
    	try {
			port = Integer.parseInt(optionService.getByKey(OptionKey.SocketPort.getKey()).getValue());

			String wsFlag = nacosConfigProp.getWsFlag();
			 //ssl证书配置--放到外部
                //InputStream pemInputStream = new FileInputStream("E:\\zhengshu\\9760642_service.sv3d.cn_nginx\\9760642_service.sv3d.cn.pem"); /// 证书存放地址
                //InputStream keyInputStream = new FileInputStream("E:\\zhengshu\\9760642_service.sv3d.cn_tomcat\\server8.key"); /// 证书存放地址
                //this.sslContext = SslContextBuilder.forServer(pemInputStream,keyInputStream).build();
                //ssl证书配置--放到项目中
                ClassPathResource pem = new ClassPathResource("service.sv3d.cn.pem");
                ClassPathResource key = new ClassPathResource("server8.key");
                this.sslContext = SslContextBuilder.forServer(pem.getInputStream(),key.getInputStream()).build();

		} catch (Exception e) {
			e.printStackTrace();
		}
        //数据量上来时设置线程池
        /*bossGroup = new NioEventLoopGroup(2,new DefaultThreadFactory("server1",true));
        workGroup = new NioEventLoopGroup(4,new DefaultThreadFactory("server2",true));*/
    	// 获取Reactor线程池
    	bossGroup = new NioEventLoopGroup();
        workGroup = new NioEventLoopGroup();
        // 服务端启动辅助类,用于设置TCP相关参数
        ServerBootstrap bootstrap = new ServerBootstrap();
        // bossGroup辅助客户端的tcp连接请求, workGroup负责与客户端之前的读写操作,设置为主从线程模型
        bootstrap.group(bossGroup,workGroup);
        //禁用nagle算法
        bootstrap.childOption(ChannelOption.TCP_NODELAY, true); 
       
        //bootstrap.childOption(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(1024*1024, 40*1024*1024)); 
        
        //当设置为true的时候,TCP会实现监控连接是否有效,当连接处于空闲状态的时候,超过了2个小时,
        //本地的TCP实现会发送一个数据包给远程的 socket,如果远程没有发回响应,TCP会持续尝试11分钟,
        //知道响应为止,如果在12分钟的时候还没响应,TCP尝试关闭socket连接。
        bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true); 
        // 设置NIO类型的channel,设置服务端NIO通信类型
        bootstrap.channel(NioServerSocketChannel.class);
        // 设置监听端口
        bootstrap.localAddress(new InetSocketAddress(port));
        // 连接到达时会创建一个通道,设置ChannelPipeline,也就是业务职责链,由处理的Handler串联而成,由从线程池处理
        bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
        	// 添加处理的Handler,通常包括消息编解码、业务处理,也可以是日志、权限、过滤等
            @Override
            protected void initChannel(SocketChannel ch) throws Exception {
                // 流水线管理通道中的处理程序(Handler),用来处理业务
                
            	  //=============================增加心跳支持============================

                //添加ssl 访问
                ch.pipeline().addLast(sslContext.newHandler(ch.alloc()));

            	// webSocket协议本身是基于http协议的,所以这边也要使用http编解码器
                ch.pipeline().addLast(new HttpServerCodec());
                ch.pipeline().addLast(new ObjectEncoder());
                //ch.pipeline().addLast(myMessageDncoder);
                //ch.pipeline().addLast(myMessageEncoder);
                // 以块的方式来写的处理器,分块向客户端写数据,防止发送大文件时导致内存溢出,channel.write(new ChunkedFile(new File("...")))
                ch.pipeline().addLast(new ChunkedWriteHandler());
                //ch.pipeline().addLast(new MyServerChunkHandler());
		        /*
		        说明:
		        1、http数据在传输过程中是分段的,HttpObjectAggregator可以将多个段聚合
		        2、这就是为什么,当浏览器发送大量数据时,就会发送多次http请求
		         */
                //需要放到HttpServerCodec后面
		        ch.pipeline().addLast(new HttpObjectAggregator(65535));//10kb?
		        //websocket数据压缩扩展,当添加这个的时候,WebSocketServerProtocolHandler第三个参数需要设置成true
		        ch.pipeline().addLast(new WebSocketServerCompressionHandler());
		        /*
		        说明:
		        1、对应webSocket,它的数据是以帧(frame)的形式传递
		        2、浏览器请求时 ws://localhost:58080/xxx 表示请求的uri
		        3、核心功能是将http协议升级为ws协议,保持长连接
		        */
		       
		        
		        //对客户端,如果在60秒内没有向服务端发送心跳,就主动断开
		        //三个参数分别为读/写/读写的空闲,我们只针对读写空闲检测
		        //ch.pipeline().addLast(new IdleStateHandler(10,11,12,TimeUnit.SECONDS));
		        //ch.pipeline().addLast(heartBeatHandler);
		        
		        // 自定义的handler,处理业务逻辑
		        //ch.pipeline().addLast(webSocketHandler);
		        //ch.pipeline().addLast(userLoginRespHandler);
		        ch.pipeline().addLast(textWebSocketHandler);
		        ch.pipeline().addLast(binaryWebSocketFrameHandler);
		        // 服务器端向外暴露的 web socket 端点,当客户端传递比较大的对象时,maxFrameSize参数的值需要调大
		        ch.pipeline().addLast(new WebSocketServerProtocolHandler(webSocketPath, webSocketProtocol, true, 10485760));//10mb?

            }
        });
        // 配置完成,开始绑定server,通过调用sync同步方法阻塞直到绑定成功
        ChannelFuture channelFuture = bootstrap.bind().sync();
        log.info("Server started and listen on:{}",channelFuture.channel().localAddress());
        // 对关闭通道进行监听
        channelFuture.channel().closeFuture().sync();
    }

4. To verify whether it works, you can use Postman or write the interface yourself.

After the configuration, I found that ws cannot be used, and I am still researching. If you have a way, you can leave a message and learn from each other. The following is the downloaded case of others, and then changed my case, so there are two servers in it, which can be used as a reference.

WebsocketServer1 is my solution. The certificate is downloaded and generated according to the certificate in the article. Start the main method of WebsocketServer1. It is possible to access it with postman, but it is not possible to use the html bound to it, but it is possible in my project.

java access case

Another way is the way that WebsocketServer generates a key and then configures a password. I have tried it, but the generated password does not match every time, so I gave up.

Guess you like

Origin blog.csdn.net/yaya_jn/article/details/130410613