Spring中STOMP的@SendToUser注解无法触发控制器方法问题
问题分析与解决方案
你当前的核心问题是:客户端直接将消息发送到STOMP消息代理的用户目的地,完全绕过了Spring控制器的@MessageMapping方法,导致后端无法对消息发送权限做校验。消息代理会直接把消息路由到目标用户的订阅队列,跳过了业务逻辑处理环节。
解决方案步骤
1. 调整客户端发送目标地址
把客户端发送的目标地址改为带/app前缀的控制器端点,让消息先经过后端处理:
function handleSubmit(e) { e.preventDefault() stompClient.publish({ destination: "/app/sendToFriend", body: JSON.stringify({ "friend": friend, "msg" : "hello there!" }) }) }
2. 编写带权限校验的控制器逻辑
注入SimpMessagingTemplate,在控制器中接收消息、完成权限校验后,再手动发送到目标用户队列:
@RestController public class StompMessageController { private final SimpMessagingTemplate messagingTemplate; public StompMessageController(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; } @MessageMapping("/sendToFriend") public void sendToFriend(MessageRequest request, Principal principal) { String sender = principal.getName(); String receiver = request.getFriend(); String msg = request.getMsg(); // 核心:添加自定义权限校验逻辑,比如检查双方是否为好友 if (!isAllowedToCommunicate(sender, receiver)) { throw new RuntimeException("无权向该用户发送消息"); } // 校验通过后,发送消息到接收方的专属队列 messagingTemplate.convertAndSendToUser( receiver, "/queue/hello", new HelloMsg("Hello, " + HtmlUtils.htmlEscape(msg)) ); } // 替换为你的实际业务校验规则 private boolean isAllowedToCommunicate(String sender, String receiver) { // 示例:暂时允许所有用户互发消息 return true; } } // 请求参数实体类 public class MessageRequest { private String friend; private String msg; // getter & setter public String getFriend() { return friend; } public void setFriend(String friend) { this.friend = friend; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
3. 确认WebSocket配置的必要性
你的配置中setApplicationDestinationPrefixes("/app")是关键,它用来区分应用端点(由Spring控制器处理)和消息代理端点(由broker直接处理)。setUserDestinationPrefix("/user")是Spring解析用户专属目的地的默认前缀,显式配置更清晰:
@Override public void configureMessageBroker(MessageBrokerRegistry config) { config.setApplicationDestinationPrefixes("/app"); // 必须,标识需路由到控制器的地址前缀 config.enableSimpleBroker("/queue"); // 启用内存消息代理,处理队列消息 config.setUserDestinationPrefix("/user"); // 可选,默认值就是/user,显式配置增强可读性 }
为什么之前的配置没触发控制器?
你之前客户端发送的/user/xxx/queue/hello属于消息代理直接处理的地址(没有/app前缀),Spring会直接把消息路由到目标用户的订阅队列,完全跳过了控制器层,所以看不到控制器的打印日志。只有带/app前缀的地址才会被转发到@MessageMapping方法处理。
内容的提问来源于stack exchange,提问作者marius627
相关产品推荐
相关产品推荐

