当前位置: 首页 > news >正文

第6篇:websocket 频道消息拦截器ChannelInterceptor 介绍和使用

ChannelInterceptor是 Spring WebSocket 中用于拦截消息通道流转的核心组件,主要作用是在消息发送 或 接收的关键节点插入自定义逻辑

核心作用

  • 拦截客户端与服务端之间的 STOMP 消息流
  • 支持在消息处理前、后或完成时执行逻辑

典型场景

  • 入站拦截(preSend):权限校验、敏感词过滤、内容审计、防刷限流
  • 出站拦截(preSend):消息脱敏、统一字段增强、推送日志记录
  • 资源清理(afterSendCompletion):释放上下文、监控埋点

关键方法:

  • preSend():最常用,返回null可拦截消息
  • 仅入站通道需处理用户会话和命令;出站通道无会话属性,用途不同

最佳实践:

  • 入站与出站逻辑分离,使用不同拦截器
  • 公共逻辑(如敏感词检测)抽成工具类,避免重复
  • 出站一般不用于拦截,而用于增强或告警


示例场景:对客户端发的非法消息进行拦截

1 写消息拦截器

import org.springframework.lang.Nullable; import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.simp.stomp.StompHeaderAccessor; import org.springframework.messaging.support.ChannelInterceptor; import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; import java.util.Map; import java.util.Set; @Component public class SocketChannelInterceptor implements ChannelInterceptor { // 敏感词集合(使用 Set 提升查找效率,O(1)) private static final Set<String> SENSITIVE_WORDS_SET = Set.of("赌博", "诈骗", "色情", "fuck", "违禁", "黑产"); /** * 消息即将被发送到通道前 * @param message * @param channel * @return */ @Nullable @Override public Message<?> preSend(Message<?> message, MessageChannel channel) { System.out.println("频道拦截器->preSend"); // 1. 快速判断:如果不是 STOMP 相关的消息(例如底层心跳包),直接放行 if (message == null) { return message; } // 包装为 STOMP 消息头访问器 StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message); // 2. 如果命令为空,说明不是标准的 STOMP 帧(比如 CONNECT 之后的心跳),直接放行 if (accessor.getCommand() == null) { return message; } // 3. 只处理 SEND 类型的消息(即客户端主动发送的消息) if (!StompCommand.SEND.equals(accessor.getCommand())) { return message; // 其他类型如 SUBSCRIBE、DISCONNECT 等不处理,直接放行 } // 4. 安全地从会话属性中获取用户名(username 是在 HandshakeInterceptor 中存入的) String username = "anonymous"; Map<String, Object> sessionAttrs = accessor.getSessionAttributes(); if (sessionAttrs != null && sessionAttrs.containsKey("username")) { Object userObj = sessionAttrs.get("username"); if (userObj != null) { username = userObj.toString(); } } // 获取消息目的地和原始负载(payload) String destination = accessor.getDestination(); Object payload = message.getPayload(); // 5. 安全提取消息内容(支持 String、byte[] 等常见类型) String content = extractContent(payload); // 6. 打印日志(生产环境建议使用 SLF4J 并控制日志级别) System.out.printf("[消息拦截] 用户: %s | 发送到: %s | 内容: %s%n", username, destination, content); // 7. 检查是否包含敏感词,若包含则拦截 if (containsSensitiveWord(content)) { System.out.printf("🚫 拦截含敏感词的消息 - 用户: %s, 目标: %s, 内容: %s%n", username, destination, content); return null; // 返回 null 表示丢弃该消息,客户端不会收到响应 } // 8. 无敏感词,放行消息 return message; } // ==================== 辅助方法 ==================== /** * 安全地从消息负载(payload)中提取可读的字符串内容 * * @param payload 消息负载,可能是 String、byte[] 或其他对象 * @return 提取后的字符串内容,若无法提取则返回空字符串或错误提示 */ private String extractContent(Object payload) { if (payload == null) { return ""; } if (payload instanceof String str) { return str; } if (payload instanceof byte[] bytes) { // 明确使用 UTF-8 编码解码,防止中文乱码 return new String(bytes, StandardCharsets.UTF_8); } // 兜底方案:尝试调用 toString()(适用于自定义 POJO,但需注意安全性) try { return payload.toString(); } catch (Exception e) { return "[内容提取失败]"; } } /** * 判断消息内容是否包含敏感词 * * @param content 消息文本内容 * @return 如果包含任意敏感词,返回 true;否则返回 false */ private boolean containsSensitiveWord(String content) { if (content == null || content.isEmpty()) return false; String cleanContent = content.replaceAll("\\s+", "") // 去掉空格 .toLowerCase(); // 转小写(针对英文) // 遍历敏感词集合,只要命中一个就返回 true for (String word : SENSITIVE_WORDS_SET) { String normalizedWord = word.toLowerCase(); if (cleanContent.contains(normalizedWord)) { return true; } } return false; } /** * 发送消息调用后立即调用 * ---- 做测试用的,可以去掉 ---- */ @Override public void postSend(Message<?> message, MessageChannel channel, boolean sent) { System.out.println("频道拦截器->postSend"); ChannelInterceptor.super.postSend(message,channel,sent); } /** * 在完成发送之后进行调用,不管是否有异常发生,一般用于资源清理 * ---- 做测试用的,可以去掉 ---- */ @Override public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, @Nullable Exception ex) { System.out.println("频道拦截器->afterSendCompletion"); ChannelInterceptor.super.afterSendCompletion(message, channel, sent, ex); } }

2 配置消息拦截器

@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry registry) { } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { } @Autowired private SocketChannelInterceptor socketChannelInterceptor; /** * 入站通道(Client Inbound Channel):客户端 → 服务端 * 配置 自己写的频道拦截器 */ @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors(socketChannelInterceptor); } }
http://www.jsqmd.com/news/472677/

相关文章:

  • 006、体系结构之TiKV读取和Coprocessor
  • python + word
  • C#毕业设计——基于C#+asp.net+SVG的基于SVG的自动站雨量分析系统设计与实现(毕业论文+程序源码)——雨量分析系统
  • 第3篇 附录:Spring Boot + WebSocket + 消息队列STOMP协议 示例-- 只有 前台页面
  • 探索Mask R-CNN:深度学习中的图像分割神器
  • Hive中rlike,like区别与使用详解
  • MAPPO动作类型改进(二)——MAPPO+连续环境
  • 2026年南京名酒回收市场选择参考:茅台、老酒、虫草及礼品回收服务指南 - 海棠依旧大
  • 多模态跟踪怎么搞?清华西电TPAMI 2025新方法深度解析,从小白到大神,吃透这一篇就够了!
  • 【Mutilism用传输门搭建D触发器/与非门/或非门】2022-3-11
  • C#毕业设计——基于C#+asp.net+SQL Server的课程指导平台设计与实现(毕业论文+程序源码)——课程指导平台
  • 2026年3月南京名酒回收机构选择指南:茅台回收、老酒回收、洋酒回收、红酒回收、虫草回收机构 - 海棠依旧大
  • 笔试题-_-
  • Simpleperf 性能工具介绍app_profiler.py -i perf.data
  • C#毕业设计——基于C#+asp.net+SQL server的通用作业批改系统设计与实现(毕业论文+程序源码)——作业批改系统
  • 2026年江苏名酒回收机构推荐榜:名酒 / 老酒 / 虫草回收、上门服务、商家选择指南,盛鑫回收用专业鉴定守护靠谱交易 - 海棠依旧大
  • anaconda常用指令
  • “水莲花数”
  • 2026年成都/自贡/内江/泸州/宜宾/乐山/四川/云南云梯车、高空车、吊车、挖掘机、压路机、铲车租赁市场盘点:如何甄选可靠服务伙伴? - 2026年企业推荐榜
  • Ubuntu 22.04 搭建onlyoffice私服
  • 欧洲智慧零售及无人店铺展代理:好评度高选择策略解析
  • Logstash 项目教程:从零开始构建数据管道
  • ubantu环境初始化
  • 零基础Java第二期:数据类型与变量
  • 2026年3月江苏名酒回收公司选择指南:茅台回收、名酒老酒回收、洋酒红酒回收、虫草回收机构 - 海棠依旧大
  • 英国伯明翰电子烟展门票办理:靠谱合作公司选择的5大核心策略
  • 2026年3月杭州租车公司选择指南:商务、婚车、大巴、考斯特、豪车、旅游包车租车公司推荐 - 海棠依旧大
  • STM32开发入门(一):在 Keil 上新建 Project 工程
  • 数据的存储(原反补码/大小端存储/截断溢出/隐式类型转换/浮点数存储)
  • 老三网址读取