小程序开发定制若依springcloud gateway nacos 集成websocket

一.代码部分

小程序开发定制相应模块里面添加3个类

WebSocketConfig,MyWebSocketHandler,WebSocketInterceptor

1.WebSocketConfig

  1. package com.ruoyi.config;
  2. import com.ruoyi.handler.MyWebSocketHandler;
  3. import com.ruoyi.interceptor.WebSocketInterceptor;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.beans.factory.annotation.Value;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.web.socket.config.annotation.EnableWebSocket;
  10. import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
  11. import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
  12. /**
  13. * 小程序开发定制首先注入一个ServerEndpointExporterBean,该Bean小程序开发定制会自动注册使用@ServerEndpoint小程序开发定制注解申明的websocket endpoint
  14. */
  15. @Configuration
  16. @EnableWebSocket
  17. public class WebSocketConfig implements WebSocketConfigurer {
  18. private static final Logger log = LoggerFactory.getLogger(WebSocketConfig.class);
  19. @Autowired
  20. private MyWebSocketHandler myWebSocketHandler;
  21. @Autowired
  22. private WebSocketInterceptor webSocketInterceptor;
  23. @Value("#{'${websocket.wsHandlers}'.split(',')}")
  24. private String[] paths;
  25. @Override
  26. public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  27. registry.addHandler(myWebSocketHandler, paths)
  28. .setAllowedOrigins("*")
  29. .addInterceptors(webSocketInterceptor);
  30. }
  31. }

2. MyWebSocketHandler

  1. package com.ruoyi.handler;
  2. import com.ruoyi.common.WebSocketCommon;
  3. import com.ruoyi.common.core.constant.WebSocketConstants;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import org.springframework.stereotype.Component;
  7. import org.springframework.web.socket.*;
  8. import java.io.IOException;
  9. @Component
  10. public class MyWebSocketHandler implements WebSocketHandler {
  11. private static final Logger log = LoggerFactory.getLogger(MyWebSocketHandler.class);
  12. @Override
  13. public void afterConnectionEstablished(WebSocketSession session) {
  14. log.info("connect websocket successful!");
  15. Object userId = session.getAttributes().get(WebSocketConstants.TOKEN);
  16. if(userId==null){
  17. return;
  18. }
  19. WebSocketSession oldSession = WebSocketCommon.CLIENT_SESSION.get(WebSocketCommon.getMapKey(session));
  20. if (oldSession != null) {
  21. log.info("close original session-start");
  22. try {
  23. oldSession.close();
  24. } catch (IOException e) {
  25. log.info("close original session failed");
  26. }
  27. }
  28. //新的会话放入
  29. WebSocketCommon.CLIENT_SESSION.put(WebSocketCommon.getMapKey(session),session);
  30. }
  31. /**
  32. * 接收客户端发送的消息-用作客户端心跳
  33. * @param session
  34. * @param message
  35. */
  36. @Override
  37. public void handleMessage(WebSocketSession session, WebSocketMessage<?> message){
  38. log.info("handle message start");
  39. try {
  40. //String mess = (String) message.getPayload(); //获取客户端发送的消息
  41. //这边可能需要处理更新map里session机制,防止map里面保存的失效,待定,等后面实际运行观察
  42. if(session.isOpen()){
  43. //心跳响应包
  44. session.sendMessage(new PongMessage());
  45. }
  46. } catch (Exception e) {
  47. log.error("e", e);
  48. }
  49. }
  50. @Override
  51. public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
  52. log.info("handle message start");
  53. if (session.isOpen()) {
  54. session.close();
  55. }
  56. log.error("connect error", exception);
  57. Object userid = session.getAttributes().get(WebSocketConstants.TOKEN).toString();
  58. if(userid==null){
  59. return;
  60. }
  61. WebSocketCommon.CLIENT_SESSION.remove(WebSocketCommon.getMapKey(session));
  62. }
  63. @Override
  64. public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus){
  65. log.error("在线人数: {}" + WebSocketCommon.CLIENT_SESSION.size());
  66. log.error("connection closed: " + closeStatus);
  67. WebSocketCommon.CLIENT_SESSION.remove(WebSocketCommon.getMapKey(session));
  68. log.error("在线人数: {}" + WebSocketCommon.CLIENT_SESSION.size());
  69. }
  70. @Override
  71. public boolean supportsPartialMessages() {
  72. return false;
  73. }
  74. }

3.WebSocketInterceptor

  1. package com.ruoyi.interceptor;
  2. import com.ruoyi.common.core.constant.WebSocketConstants;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. import org.springframework.http.server.ServerHttpRequest;
  6. import org.springframework.http.server.ServerHttpResponse;
  7. import org.springframework.http.server.ServletServerHttpRequest;
  8. import org.springframework.stereotype.Component;
  9. import org.springframework.web.socket.WebSocketHandler;
  10. import org.springframework.web.socket.server.HandshakeInterceptor;
  11. import java.util.Map;
  12. @Component
  13. public class WebSocketInterceptor implements HandshakeInterceptor {
  14. private static final Logger log = LoggerFactory.getLogger(WebSocketInterceptor.class);
  15. //在握手之前执行该方法, 继续握手返回true, 中断握手返回false. 通过attributes参数设置WebSocketSession的属性
  16. @Override
  17. public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes){
  18. if (request instanceof ServletServerHttpRequest) {
  19. String uri = request.getURI().getPath();
  20. String token = uri.substring(uri.lastIndexOf("/")+1);
  21. attributes.put(WebSocketConstants.TOKEN,token);
  22. log.info("current token is:"+token);
  23. }
  24. return true;
  25. }
  26. @Override
  27. public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
  28. log.info("coming webSocketInterceptor afterHandshake method...");
  29. }
  30. }

二.中关键配置

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

  1. # websocket模块
  2. - id: ruoyi-websocket
  3. uri: lb:ws://ruoyi-collection
  4. predicates:
  5. - Path=/ws/**
  6. - id: ruoyi-websocket
  7. uri: lb:ws://ruoyi-collection
  8. predicates:
  9. - Path=/ws1/**

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

  1. websocket:
  2. 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

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

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

  1. # 不校验白名单
  2. ignore:
  3. whites:
  4. - /auth/logout
  5. - /auth/login
  6. - /auth/register
  7. - /*/v2/api-docs
  8. - /csrf
  9. - /collection/sendBoxStatus
  10. - /ws/**
  11. - /ws1/**

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

WebSocketConstants

  1. package com.ruoyi.common.core.constant;
  2. public class WebSocketConstants {
  3. //用户标识
  4. public static String CLIENT_FLAG = "clientId";
  5. //用户标识key
  6. public static String TOKEN = "token";
  7. //每个连接key前缀标识
  8. public static String PREFIX = "prefix";
  9. }
WebSocketCommon
  1. package com.ruoyi.common;
  2. import com.ruoyi.common.core.constant.WebSocketConstants;
  3. import org.springframework.web.socket.WebSocketSession;
  4. import java.util.concurrent.ConcurrentHashMap;
  5. public class WebSocketCommon {
  6. /**
  7. * 保存已登录的会话session
  8. */
  9. public static ConcurrentHashMap<String, WebSocketSession> CLIENT_SESSION = new ConcurrentHashMap();
  10. public static String getMapKey(WebSocketSession session){
  11. Object userId = session.getAttributes().get(WebSocketConstants.TOKEN);
  12. if(userId==null){
  13. return null;
  14. }
  15. String useridStr = userId.toString();
  16. return useridStr;
  17. }
  18. }

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

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发