基于WebSocket的web端IM即时通讯应用的开发-精进


基于WebSocket的web端IM即时通讯应用的开发-精进

上篇关于websocket的文章,登录确认session是通过与springsecurity结合,其登录后默认的session实现了principle,自动就能转换为websocketsession。

而当前的需求是自己实现的简单登录创建的httpsession怎么转换到websocketsession。

跟着下面的例子模拟体验

登录的时候,创建session 并存用户唯一标识

HttpSession httpsession = request.getSession();
httpsession.setAttribute("userName",username);

通过修改websocketconfig,最终将当前登录连接的websocket调整成跟用户名相关的连接。

//SessionAuthHandshakeInterceptor 定制的是simpSessionAttributes
    //setHandshakeHandler 定制的是simpUser
    registry.addEndpoint("/im-websocket").setAllowedOrigins("*").addInterceptors(new SessionAuthHandshakeInterceptor())
            .setHandshakeHandler(new DefaultHandshakeHandler(){
              @Override
              protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
                String userName=  (String)attributes.get("userName");
                UserPrincipal  userPrincipal=new UserPrincipal(userName);
                return userPrincipal;
              }
            })
            .withSockJS();
            
            其中SessionAuthHandshakeInterceptor核心代码,其将session的username转存到websocketsession的参数里。由上面的determineUser转为实现了Principal的用户类里。
            public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        HttpSession session = getSession(request);
        if(session == null ){
            return false;
        }
        attributes.put("userName",session.getAttribute("userName").toString());
        return true; 
    }

监控链接情况

以下演示以两个用户分别连接、订阅用户列表公共通道,订阅私聊通道,发送单条信息为例

连接成功sessionid:333qmepi 地址:ws://localhost:8080/cepm/im-websocket/232/333qmepi/websocket
连接成功:王朝  
登录服务 : GenericMessage [payload=byte[0], headers={simpMessageType=CONNECT, stompCommand=CONNECT, nativeHeaders={accept-version=[1.1,1.0], heart-beat=[10000,10000]}, simpSessionAttributes={userName=王朝}, simpHeartbeat=[J@39c5387a, simpUser=com.haiyisoft.cepm.config.websoket.UserPrincipal@d071c92, simpSessionId=333qmepi}]
登录服务结束: GenericMessage [payload=byte[0], headers={simpMessageType=CONNECT_ACK, simpConnectMessage=GenericMessage [payload=byte[0], headers={spanTraceId=83fe33462c0494b6, spanId=83fe33462c0494b6, simpMessageType=CONNECT, stompCommand=CONNECT, nativeHeaders={accept-version=[1.1,1.0], heart-beat=[10000,10000]}, simpSessionAttributes={userName=王朝}, simpHeartbeat=[J@39c5387a, messageSent=true, simpUser=com.haiyisoft.cepm.config.websoket.UserPrincipal@d071c92, spanSampled=0, simpSessionId=333qmepi}], simpUser=com.haiyisoft.cepm.config.websoket.UserPrincipal@d071c92, simpSessionId=333qmepi}]
订阅 : GenericMessage [payload=byte[0], headers={simpMessageType=SUBSCRIBE, stompCommand=SUBSCRIBE, nativeHeaders={id=[sub-0], destination=[/user/topic/private]}, simpSessionAttributes={userName=王朝}, simpHeartbeat=[J@6ed3203d, simpSubscriptionId=sub-0, simpUser=com.haiyisoft.cepm.config.websoket.UserPrincipal@d071c92, simpSessionId=333qmepi, simpDestination=/user/topic/private}]
订阅 : GenericMessage [payload=byte[0], headers={simpMessageType=SUBSCRIBE, stompCommand=SUBSCRIBE, nativeHeaders={id=[sub-1], destination=[/topic/userlist]}, simpSessionAttributes={userName=王朝}, simpHeartbeat=[J@7c7dec8a, simpSubscriptionId=sub-1, simpUser=com.haiyisoft.cepm.config.websoket.UserPrincipal@d071c92, simpSessionId=333qmepi, simpDestination=/topic/userlist}]

第二个人登录并订阅

影响第一个人的公共订阅回复: GenericMessage [payload=byte[47], headers={simpMessageType=MESSAGE, simpSubscriptionId=sub-1, contentType=application/json;charset=UTF-8, simpSessionId=333qmepi, simpDestination=/topic/userlist}]
连接成功sessionid:1nk35gag  ws://localhost:8080/cepm/im-websocket/540/1nk35gag/websocket
连接成功:马汉
登录服务 : GenericMessage [payload=byte[0], headers={simpMessageType=CONNECT, stompCommand=CONNECT, nativeHeaders={accept-version=[1.1,1.0], heart-beat=[10000,10000]}, simpSessionAttributes={userName=马汉}, simpHeartbeat=[J@4023e40c, simpUser=com.haiyisoft.cepm.config.websoket.UserPrincipal@46d667d0, simpSessionId=1nk35gag}]
登录服务结束 : GenericMessage [payload=byte[0], headers={simpMessageType=CONNECT_ACK, simpConnectMessage=GenericMessage [payload=byte[0], headers={spanTraceId=4ccc5821d1d65934, spanId=4ccc5821d1d65934, simpMessageType=CONNECT, stompCommand=CONNECT, nativeHeaders={accept-version=[1.1,1.0], heart-beat=[10000,10000]}, simpSessionAttributes={userName=马汉}, simpHeartbeat=[J@4023e40c, messageSent=true, simpUser=com.haiyisoft.cepm.config.websoket.UserPrincipal@46d667d0, spanSampled=0, simpSessionId=1nk35gag}], simpUser=com.haiyisoft.cepm.config.websoket.UserPrincipal@46d667d0, simpSessionId=1nk35gag}]
订阅 : GenericMessage [payload=byte[0], headers={simpMessageType=SUBSCRIBE, stompCommand=SUBSCRIBE, nativeHeaders={id=[sub-0], destination=[/user/topic/private]}, simpSessionAttributes={userName=马汉}, simpHeartbeat=[J@19c4bde5, simpSubscriptionId=sub-0, simpUser=com.haiyisoft.cepm.config.websoket.UserPrincipal@46d667d0, simpSessionId=1nk35gag, simpDestination=/user/topic/private}]
订阅 : GenericMessage [payload=byte[0], headers={simpMessageType=SUBSCRIBE, stompCommand=SUBSCRIBE, nativeHeaders={id=[sub-1], destination=[/topic/userlist]}, simpSessionAttributes={userName=马汉}, simpHeartbeat=[J@34a6e51a, simpSubscriptionId=sub-1, simpUser=com.haiyisoft.cepm.config.websoket.UserPrincipal@46d667d0, simpSessionId=1nk35gag, simpDestination=/topic/userlist}]

第一个给第二个发私信

第一个人发送开启 : GenericMessage [payload=byte[82], headers={simpMessageType=MESSAGE, stompCommand=SEND, nativeHeaders={destination=[/app/private], content-length=[82]}, simpSessionAttributes={userName=王朝}, simpHeartbeat=[J@f3b84e7, simpUser=com.haiyisoft.cepm.config.websoket.UserPrincipal@d071c92, simpSessionId=333qmepi, simpDestination=/app/private}]
发送结束: GenericMessage [payload=byte[94], headers={simpMessageType=MESSAGE, nativeHeaders={simpOrigDestination=[/user/topic/private]}, simpSubscriptionId=sub-0, contentType=application/json;charset=UTF-8, simpSessionId=1nk35gag, simpDestination=/topic/private-user1nk35gag}]

可以看出来给哪个人发消息,谁能收到,就是跟simpUser这个值有关,如果里面的username和发送的时候写的一致,则他就可以接收到。
template.convertAndSendToUser(“接收人username”,"/topic/private",message)

第一段截取的代码不是最全,请知晓。

另外websocket在springboot1.5的自带tomcat下有问题,换成undertow

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
          <exclusions>
            <exclusion>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-tomcat</artifactId>
            </exclusion>
          </exclusions>
        </dependency>
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-undertow</artifactId>
    </dependency>

相关推荐