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

Spring WebSockets如何验证消息并筛选接收客户端?

当然支持啦!Spring WebSockets给你提供了好几种灵活的方式来实现这种「仅指定客户端接收敏感消息」的需求,我给你整理了几个常用的实现方案:

方案一:用MessageInterceptor拦截并过滤消息

这是最通用的方式——通过拦截入站/出站消息,在消息发送前校验客户端权限,决定是否放行。你可以实现ChannelInterceptor接口,在消息到达Broker之前做过滤:

@Component
public class SensitiveMessageFilter implements ChannelInterceptor {

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        
        // 只处理发送到主题的消息(根据你的场景调整)
        if (StompCommand.SEND.equals(accessor.getCommand()) || StompCommand.MESSAGE.equals(accessor.getCommand())) {
            // 解析消息内容,提取敏感标识和允许接收的客户端信息
            String payload = new String((byte[]) message.getPayload());
            // 获取当前客户端的会话ID或用户信息(如果已经和用户绑定)
            String clientSessionId = accessor.getSessionId();
            String currentUser = accessor.getUser() != null ? accessor.getUser().getName() : null;

            // 替换成你的权限校验逻辑:比如判断当前客户端是否在允许接收的列表里
            if (!isAuthorized(currentUser, clientSessionId, payload)) {
                // 无权限则拦截消息,返回null即可
                return null;
            }
        }
        return message;
    }

    private boolean isAuthorized(String user, String sessionId, String payload) {
        // 这里写你的具体校验逻辑:比如从payload里提取允许的用户ID/会话ID,和当前客户端匹配
        // 示例:假设payload里包含"allowedClients"字段,解析后判断是否包含当前sessionId
        return true;
    }
}

然后在WebSocket配置类里注册这个拦截器:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Autowired
    private SensitiveMessageFilter sensitiveMessageFilter;

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        // 注册拦截器处理入站消息
        registration.interceptors(sensitiveMessageFilter);
    }

    // 其他WebSocket配置,比如设置代理、允许的起源等...
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic", "/queue");
        config.setApplicationDestinationPrefixes("/app");
    }
}
方案二:@SendToUser + 权限校验(针对特定用户场景)

如果你的敏感消息本来就是要发给特定用户,而不是广播到主题,那直接用@SendToUser会更简单——它会把消息发送到该用户专属的目的地(格式为/user/{username}/queue/xxx),只有该用户的客户端能收到。同时你可以在发送前做权限校验:

@Controller
public class ContractMessageController {

    @MessageMapping("/app/send-sensitive-contract")
    @SendToUser("/queue/sensitive-contracts")
    public String sendSensitiveContract(@Payload String contractMessage, Principal principal) {
        // 校验当前用户是否有权限接收这条消息
        if (!hasPermissionToView(principal.getName(), contractMessage)) {
            throw new AccessDeniedException("你无权查看该敏感合约消息");
        }
        return contractMessage;
    }

    private boolean hasPermissionToView(String username, String contractMessage) {
        // 你的权限逻辑:比如解析合约ID,判断用户是否属于该合约的授权查看者
        return true;
    }
}

客户端订阅的时候,只需要订阅/user/queue/sensitive-contracts就能收到自己专属的敏感消息,不用再担心其他客户端获取到。

方案三:自定义Broker消息处理器(广播主题的细粒度过滤)

如果你的需求是广播到/topic/contract/{contractId}这类主题,但需要给每个订阅该主题的客户端做权限校验,那可以自定义Broker的消息处理逻辑。比如扩展SimpleBrokerMessageHandler,在广播消息前逐个判断订阅者是否有权限:

public class SecuredSimpleBrokerMessageHandler extends SimpleBrokerMessageHandler {

    public SecuredSimpleBrokerMessageHandler(MessageBrokerRegistry registry, SubscribableChannel clientOutboundChannel,
                                             TaskScheduler scheduler) {
        super(registry, clientOutboundChannel, scheduler);
    }

    @Override
    protected void sendMessageToSubscribers(String destination, Message<?> message, Set<String> sessionIds) {
        // 过滤出有权限接收该消息的会话ID
        Set<String> authorizedSessionIds = new HashSet<>();
        for (String sessionId : sessionIds) {
            // 获取会话对应的用户信息,判断是否有权限接收当前消息
            if (isSessionAuthorized(sessionId, destination, message)) {
                authorizedSessionIds.add(sessionId);
            }
        }
        // 只给有权限的会话发送消息
        super.sendMessageToSubscribers(destination, message, authorizedSessionIds);
    }

    private boolean isSessionAuthorized(String sessionId, String destination, Message<?> message) {
        // 这里写你的校验逻辑:比如根据destination里的contractId,判断该会话用户是否有权限查看
        return true;
    }
}

然后在配置类里替换默认的Broker处理器:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic", "/queue");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Bean
    public SimpleBrokerMessageHandler simpleBrokerMessageHandler(MessageBrokerRegistry registry,
                                                                @Qualifier("clientOutboundChannel") SubscribableChannel clientOutboundChannel,
                                                                TaskScheduler messageBrokerTaskScheduler) {
        return new SecuredSimpleBrokerMessageHandler(registry, clientOutboundChannel, messageBrokerTaskScheduler);
    }
}

这个方案适合需要对广播主题做细粒度权限控制的场景,但实现起来相对复杂一些。


总结一下:这三个方案覆盖了大部分场景——通用消息拦截用方案一,特定用户发送用方案二,广播主题细粒度过滤用方案三。你可以根据自己的业务需求选择最合适的方式。

内容的提问来源于stack exchange,提问作者Val Schuman

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 06:23:15