赞
踩
相应模块里面添加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...");
- }
-
- }

在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/xxx 和ws://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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。