若依springcloud gateway nacos 集成websocket

一.代码部分

相应模块里面添加3个类

WebSocketConfig,MyWebSocketHandler,WebSocketInterceptor

1.WebSocketConfig

package com.ruoyi.config;

import com.ruoyi.handler.MyWebSocketHandler;
import com.ruoyi.interceptor.WebSocketInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

/**
 * 首先注入一个ServerEndpointExporterBean,该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    private static final Logger log = LoggerFactory.getLogger(WebSocketConfig.class);

    @Autowired
    private MyWebSocketHandler myWebSocketHandler;

    @Autowired
    private WebSocketInterceptor webSocketInterceptor;

    @Value("#{'${websocket.wsHandlers}'.split(',')}")
    private String[] paths;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myWebSocketHandler, paths)
                .setAllowedOrigins("*")
                .addInterceptors(webSocketInterceptor);
    }
}

2. MyWebSocketHandler

package com.ruoyi.handler;

import com.ruoyi.common.WebSocketCommon;
import com.ruoyi.common.core.constant.WebSocketConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.*;

import java.io.IOException;

@Component
public class MyWebSocketHandler implements WebSocketHandler {

    private static final Logger log = LoggerFactory.getLogger(MyWebSocketHandler.class);

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        log.info("connect websocket successful!");
        Object userId = session.getAttributes().get(WebSocketConstants.TOKEN);
        if(userId==null){
            return;
        }
        WebSocketSession oldSession = WebSocketCommon.CLIENT_SESSION.get(WebSocketCommon.getMapKey(session));
        if (oldSession != null) {
            log.info("close original session-start");
            try {
                oldSession.close();
            } catch (IOException e) {
                log.info("close original session failed");
            }
        }
        //新的会话放入
        WebSocketCommon.CLIENT_SESSION.put(WebSocketCommon.getMapKey(session),session);
    }

    /**
     * 接收客户端发送的消息-用作客户端心跳
     * @param session
     * @param message
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message){
        log.info("handle message start");
        try {
            //String mess = (String) message.getPayload();  //获取客户端发送的消息
            //这边可能需要处理更新map里session机制,防止map里面保存的失效,待定,等后面实际运行观察
            if(session.isOpen()){
                //心跳响应包
                session.sendMessage(new PongMessage());
            }
        } catch (Exception e) {
            log.error("e", e);
        }
    }

    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        log.info("handle message start");
        if (session.isOpen()) {
            session.close();
        }
        log.error("connect error", exception);
        Object userid = session.getAttributes().get(WebSocketConstants.TOKEN).toString();
        if(userid==null){
            return;
        }
        WebSocketCommon.CLIENT_SESSION.remove(WebSocketCommon.getMapKey(session));
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus){
        log.error("在线人数: {}" + WebSocketCommon.CLIENT_SESSION.size());
        log.error("connection closed: " + closeStatus);
        WebSocketCommon.CLIENT_SESSION.remove(WebSocketCommon.getMapKey(session));
        log.error("在线人数: {}" + WebSocketCommon.CLIENT_SESSION.size());
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}

3.WebSocketInterceptor

package com.ruoyi.interceptor;

import com.ruoyi.common.core.constant.WebSocketConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import java.util.Map;

@Component
public class WebSocketInterceptor implements HandshakeInterceptor {

    private static final Logger log = LoggerFactory.getLogger(WebSocketInterceptor.class);

    //在握手之前执行该方法, 继续握手返回true, 中断握手返回false. 通过attributes参数设置WebSocketSession的属性
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes){
        if (request instanceof ServletServerHttpRequest) {
            String uri = request.getURI().getPath();
            String token = uri.substring(uri.lastIndexOf("/")+1);
            attributes.put(WebSocketConstants.TOKEN,token);
            log.info("current token is:"+token);
        }
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
        log.info("coming webSocketInterceptor afterHandshake method...");
    }

}

二.nacos中关键配置

在ruoyi-gateway-dev.yml 配置文件中添加如下,我是在ruoyi-collection这个模块中使用websocket的,所以配置路由到这个服务

        # websocket模块
        - id: ruoyi-websocket
          uri: lb:ws://ruoyi-collection
          predicates:
            - Path=/ws/**
        - id: ruoyi-websocket
          uri: lb:ws://ruoyi-collection
          predicates:
            - Path=/ws1/**  

这边是加了2个ws和ws1路径的配置,然后这样访问ws://ip+port/ws/xxxws://ip+port/ws1/xxx都能连接上,这边注意,配置如下,ruoyi-collection是我新加的模块,这个模块中使用到了,故我在这个模块的配置文件中添加如下配置,{token} 这个表示连接时是动态传的变量

websocket:
  wsHandlers: /ws/{token},/ws1/{token}

三.其它说明

1.在ruoyi-gateway-dev.yml中上面的配置中加了下面

filters:

- StripPrefix=1

这个配置,会导致连不上,要注意。

2.如果需要授权才能连接,我是这么实现的,我连接地址后面拼了一个token,例如ws://ip+port/ws/xxx?token=xxxxxx,这样在网关那边鉴权的时候,获取连接后面的token,拿到后正常往后面走,贴出关键代码如下

在gateway模块中

com.ruoyi.gateway.filter.AuthFilter#getToken

private String getToken(ServerHttpRequest request)
    {
        String token = request.getHeaders().getFirst(TokenConstants.AUTHENTICATION);
        if(StringUtils.isEmpty(token)){
            //尝试从拼接参数中获取token,这步是为了websocket鉴权
            Object authorization = request.getQueryParams().get(TokenConstants.AUTHENTICATION);
            if(authorization!=null){
                token = ((List<?>) authorization).get(0).toString();
            }
        }
        // 如果前端设置了令牌前缀,则裁剪掉前缀
        if (StringUtils.isNotEmpty(token) && token.startsWith(TokenConstants.PREFIX))
        {
            token = token.replaceFirst(TokenConstants.PREFIX, StringUtils.EMPTY);
        }
        return token;
    }

如果不需要授权的话,则在ruoyi-gateway-dev.yml中配置下白名单即可

  # 不校验白名单
  ignore:
    whites:
      - /auth/logout
      - /auth/login
      - /auth/register
      - /*/v2/api-docs
      - /csrf
      - /collection/sendBoxStatus
      - /ws/**
      - /ws1/**

3.这边补上上面代码使用到的新加的类,供参考

WebSocketConstants

package com.ruoyi.common.core.constant;

public class WebSocketConstants {

    //用户标识
    public static String CLIENT_FLAG = "clientId";

    //用户标识key
    public static String TOKEN = "token";

    //每个连接key前缀标识
    public static String PREFIX = "prefix";
}
WebSocketCommon
package com.ruoyi.common;

import com.ruoyi.common.core.constant.WebSocketConstants;
import org.springframework.web.socket.WebSocketSession;

import java.util.concurrent.ConcurrentHashMap;

public class WebSocketCommon {

    /**
     * 保存已登录的会话session
     */
    public static ConcurrentHashMap<String, WebSocketSession> CLIENT_SESSION = new ConcurrentHashMap();

    public static String getMapKey(WebSocketSession session){
        Object userId = session.getAttributes().get(WebSocketConstants.TOKEN);
        if(userId==null){
            return null;
        }
        String useridStr = userId.toString();
        return useridStr;
    }
}

最后非常感谢这篇文章的作者,参照这篇文章实现了功能,另外有什么问题欢迎留言,一起探讨。

SpringCloud Gateway 集成 WebSocket服务_哦哈哟小朋友的博客-CSDN博客_gateway整合websocket

  • 1
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 17
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值