Kaynağa Gözat

chat添加ssl加密

udream-cxs 4 yıl önce
ebeveyn
işleme
80b8ff0a94

+ 39 - 31
lift-chat-service/src/main/java/cn/com/ty/chat/neety/server/ChatServer.java

@@ -1,6 +1,5 @@
 package cn.com.ty.chat.neety.server;
 
-import cn.com.ty.chat.neety.server.handler.WebSocketHandler;
 import cn.com.ty.chat.utils.RemotingUtil;
 import io.netty.bootstrap.ServerBootstrap;
 import io.netty.channel.*;
@@ -8,16 +7,21 @@ import io.netty.channel.epoll.Epoll;
 import io.netty.channel.epoll.EpollEventLoopGroup;
 import io.netty.channel.epoll.EpollServerSocketChannel;
 import io.netty.channel.nio.NioEventLoopGroup;
-import io.netty.channel.socket.SocketChannel;
 import io.netty.channel.socket.nio.NioServerSocketChannel;
-import io.netty.handler.codec.http.HttpObjectAggregator;
-import io.netty.handler.codec.http.HttpServerCodec;
-import io.netty.handler.stream.ChunkedWriteHandler;
-import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslContextBuilder;
 import lombok.extern.slf4j.Slf4j;
 
-import javax.annotation.PreDestroy;
-import java.util.concurrent.TimeUnit;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLException;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+import java.security.cert.CertificateException;
 
 @Slf4j
 public class ChatServer {
@@ -36,7 +40,7 @@ public class ChatServer {
      *
      * @throws InterruptedException
      */
-    public void start(Integer port, Integer readerIdleTime) throws InterruptedException {
+    public void start(Integer port, Integer readerIdleTime, String sslPath, String sslPassword) throws InterruptedException {
         try {
             if (useEpoll()) {
                 bossGroup = new EpollEventLoopGroup();
@@ -45,6 +49,16 @@ public class ChatServer {
                 bossGroup = new NioEventLoopGroup();
                 workerGroup = new NioEventLoopGroup();
             }
+
+            //构建ssl,适配wss请求
+            KeyStore keyStore = KeyStore.getInstance("JKS");
+            keyStore.load(new FileInputStream(sslPath), sslPassword.toCharArray());
+
+            KeyManagerFactory keyManagerFactory = null;
+            keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
+            keyManagerFactory.init(keyStore, sslPassword.toCharArray());
+
+            SslContext sslContext = SslContextBuilder.forServer(keyManagerFactory).build();
             // netty服务,启动引擎
             ServerBootstrap b = new ServerBootstrap();
             // 主从模式
@@ -54,32 +68,26 @@ public class ChatServer {
                 // 配置信息
                 .option(ChannelOption.SO_BACKLOG, 1024)// 针对主线程配置
                 // .childOption(ChannelOption.SO_KEEPALIVE,true);//子线程配置
-                // 子线程的处理,Handler
-                .childHandler(new ChannelInitializer<SocketChannel>() {
-
-                    @Override
-                    protected void initChannel(SocketChannel channel) throws InterruptedException {
-                        ChannelPipeline pipeline = channel.pipeline();
-                        // 读、取、读取 心跳检测
-//                         pipeline.addLast("idle", new ImIdleHandler(2000, 2500, 3000));
-                        pipeline.addLast("ping", new IdleStateHandler(readerIdleTime, 0, 0, TimeUnit.SECONDS));
-                        // Http消息编码解码
-                        pipeline.addLast("http-codec", new HttpServerCodec());
-                        // 参数含义是消息合并的数据大小,如此代表聚合的消息内容长度不超过65536kb。
-                        pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
-                        // WebSocket通信支持
-                        pipeline.addLast("http-chunked", new ChunkedWriteHandler());
-                        // http请求的业务逻辑处理 WebSocket服务端Handler
-                        pipeline.addLast("handler", new WebSocketHandler());
-                    }
-
-                });
 
+                // 子线程的处理,Handler
+                .childHandler(new MyInitChannel(sslContext, readerIdleTime));
             // 等待客户端连接
             ChannelFuture future = b.bind(port).sync();
-            log.info("websoket-server started successful. port:{} ", port);
+            log.info("websocket-server started successful. port:{} ", port);
             future.channel().closeFuture().sync();
-        } catch (InterruptedException e) {
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (KeyStoreException e) {
+            e.printStackTrace();
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        } catch (CertificateException e) {
+            e.printStackTrace();
+        } catch (SSLException e) {
+            e.printStackTrace();
+        } catch (UnrecoverableKeyException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
             e.printStackTrace();
         } finally {
             bossGroup.shutdownGracefully().sync();

+ 46 - 0
lift-chat-service/src/main/java/cn/com/ty/chat/neety/server/MyInitChannel.java

@@ -0,0 +1,46 @@
+package cn.com.ty.chat.neety.server;
+
+import cn.com.ty.chat.neety.server.handler.WebSocketHandler;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpServerCodec;
+import io.netty.handler.ssl.SslContext;
+import io.netty.handler.ssl.SslHandler;
+import io.netty.handler.stream.ChunkedWriteHandler;
+import io.netty.handler.timeout.IdleStateHandler;
+
+import javax.net.ssl.SSLEngine;
+import java.util.concurrent.TimeUnit;
+
+public class MyInitChannel extends ChannelInitializer {
+    private final SslContext context;
+    private final int readerIdleTime;
+
+    public MyInitChannel(SslContext context, int readerIdleTime) {
+        this.context = context;
+        this.readerIdleTime = readerIdleTime;
+    }
+
+    @Override
+    protected void initChannel(Channel channel) throws Exception {
+        ChannelPipeline pipeline = channel.pipeline();
+        if (context != null){
+            SSLEngine sslEngine = context.newEngine(channel.alloc());
+            sslEngine.setNeedClientAuth(false);
+            sslEngine.setUseClientMode(false);
+            pipeline.addLast("ssl", new SslHandler(sslEngine));
+        }
+        // 读、取、读取 心跳检测
+        pipeline.addLast("ping", new IdleStateHandler(readerIdleTime, 0, 0, TimeUnit.SECONDS));
+        // Http消息编码解码
+        pipeline.addLast("http-codec", new HttpServerCodec());
+        // 参数含义是消息合并的数据大小,如此代表聚合的消息内容长度不超过65536kb。
+        pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
+        // WebSocket通信支持
+        pipeline.addLast("http-chunked", new ChunkedWriteHandler());
+        // http请求的业务逻辑处理 WebSocket服务端Handler
+        pipeline.addLast("handler", new WebSocketHandler());
+    }
+}

+ 7 - 1
lift-chat-service/src/main/java/cn/com/ty/chat/neety/server/NettyListener.java

@@ -27,11 +27,17 @@ public class NettyListener implements CommandLineRunner {
     @Value("${netty.readerIdleTime}")
     private Integer readerIdleTime;
 
+    @Value("${netty.path}")
+    private String sslPath;
+
+    @Value("${netty.password}")
+    private String sslPassword;
+
     @Override
     public void run(String... args) throws Exception {
         new Thread(() -> {
             try {
-                new ChatServer().start(port, readerIdleTime);
+                new ChatServer().start(port, readerIdleTime, sslPath, sslPassword);
             } catch (Exception e) {
                 e.printStackTrace();
             }

+ 25 - 10
lift-chat-service/src/main/java/cn/com/ty/chat/neety/server/handler/WebSocketHandler.java

@@ -10,6 +10,7 @@ import io.netty.buffer.Unpooled;
 import io.netty.channel.*;
 import io.netty.handler.codec.http.*;
 import io.netty.handler.codec.http.websocketx.*;
+import io.netty.handler.ssl.SslHandshakeCompletionEvent;
 import io.netty.handler.timeout.IdleState;
 import io.netty.handler.timeout.IdleStateEvent;
 import io.netty.util.CharsetUtil;
@@ -44,13 +45,13 @@ public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {
      */
     private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
         // 如果HTTP解码失败,返回HHTP异常
-        if (!request.getDecoderResult().isSuccess() || (!"websocket".equals(request.headers().get("Upgrade")))) {
+        if (!request.decoderResult().isSuccess() || (!"websocket".equals(request.headers().get("Upgrade")))) {
             sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
             return;
         }
 
         // 正常WebSocket的Http连接请求,构造握手响应返回
-        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://" + request.headers().get(HttpHeaders.Names.HOST), null, false);
+        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("wss://" + request.headers().get(HttpHeaders.Names.HOST), null, false);
         handShaker = wsFactory.newHandshaker(request);
         if (handShaker == null) { // 无法处理的websocket版本
             WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
@@ -175,29 +176,43 @@ public class WebSocketHandler extends SimpleChannelInboundHandler<Object> {
             if (event.state()== IdleState.READER_IDLE){
                 log.info("{}-----》客户端操作空闲",process.getNickName(ctx.channel()));
                 process.logout(ctx.channel());
-                CloseWebSocketFrame fram = new CloseWebSocketFrame();
-                handShaker.close(ctx.channel(), fram.retain());
+//                CloseWebSocketFrame fram = new CloseWebSocketFrame();
+//                handShaker.close(ctx.channel(), fram.retain());
             }
         }
+        if (evt instanceof SslHandshakeCompletionEvent){
+            log.info("{}-----》ssl链接超时");
+            SslHandshakeCompletionEvent handshake = (SslHandshakeCompletionEvent) evt;
+            if (handshake.isSuccess()) {
+                ctx.fireChannelActive();
+            } else{
+                ctx.fireExceptionCaught(handshake.cause());
+            }
+//            process.logout(ctx.channel());
+        }
     }
 
-        @Override
+    @Override
     public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
-        log.info("{}-----》客户端手动退出",process.getNickName(ctx.channel()));
+        log.info("{}-----》客户端手动退出", process.getNickName(ctx.channel()));
+
+        //telnet 不会进入channelRead0方法,因此不会构建handshaker
+        if (handShaker != null){
+            CloseWebSocketFrame fram = new CloseWebSocketFrame();
+            handShaker.close(ctx.channel(), fram.retain());
+        }
         process.logout(ctx.channel());
-        CloseWebSocketFrame fram = new CloseWebSocketFrame();
-        handShaker.close(ctx.channel(), fram.retain());
     }
 
     @Override
     public void channelInactive(ChannelHandlerContext ctx) throws Exception {
-        log.info("{}-----》客户端不活跃",process.getNickName(ctx.channel()));
+        log.info("{}-----》客户端主动断链",process.getNickName(ctx.channel()));
     }
 
     @Override
     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
 //        super.exceptionCaught(ctx, cause);
-        log.info("客户端异常---->"+cause.getMessage());
+        log.info("客户端异常---->" + cause.getMessage());
         cause.printStackTrace();
 //        process.logout(ctx.channel());
     }

+ 1 - 1
lift-ud-service/src/main/resources/alipay/alipay-online.properties

@@ -17,7 +17,7 @@ alipay.alipay-root-cert-path=/root/server/cert/alipayRootCert.crt
 alipay.open-api-domain=https://openapi.alipay.com/gateway.do
 # \u652f\u4ed8\u5f02\u6b65\u56de\u8c03\u5730\u5740
 #alipay.alipay-notify-url=http://lift.whlhcx.com/api/alipayNotify
-alipay.alipay-notify-url=http://111.47.29.214:10227/udream/api/alipayNotify
+alipay.alipay-notify-url=http://app4.edtyun.com:10227/udream/api/alipayNotify
 
 alipay.charset=utf-8
 

+ 1 - 1
lift-ud-service/src/main/resources/application.yml

@@ -8,7 +8,7 @@ spring:
   application:
     name: lift-ud-service
   profiles:
-    active: test
+    active: internal
   servlet:  #设置上传APP的大小限制
     multipart:
       max-file-size: 500MB

+ 1 - 1
lift-ud-service/src/main/resources/bootstrap.yml

@@ -21,6 +21,6 @@ eureka:
     lease-expiration-duration-in-seconds: 20
   client:
     service-url:
-      defaultZone: http://192.168.1.121:10225/eureka/
+      defaultZone: http://172.16.24.142:10225/eureka/