You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 18:55:17