You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot中WebSocket连接403 Forbidden问题及STOMP协议后续请求认证方案咨询

Spring Boot中WebSocket连接403 Forbidden问题及STOMP协议后续请求认证方案咨询

一、先解决你遇到的WebSocket握手403 Forbidden问题

你已经在SecurityConfiguration里给/ws**设了permitAll(),但还是403,大概率是因为Spring Security对WebSocket有独立的安全配置链,仅靠HttpSecurity的配置不够。你需要额外配置WebSocket的安全规则,具体可以通过实现SecurityWebSocketMessageBrokerConfigurer来完成:

@Configuration
public class WebSocketSecurityConfig implements SecurityWebSocketMessageBrokerConfigurer {

    @Override
    public void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
        // 结合HttpSecurity的握手permitAll配置,这里设置消息层面的权限规则
        messages
                // 允许所有订阅/发送到/user/**的消息(对应你的Broker前缀)
                .simpSubscribeDestMatchers("/user/**").permitAll()
                // 要求所有/app/**的消息必须认证(对应你的@MessageMapping接口路径)
                .simpDestMatchers("/app/**").authenticated()
                // 拒绝其他所有STOMP消息
                .anyMessage().denyAll();
    }
}

另外检查下CORS配置的一致性:你的WebSocketConfigsetAllowedOriginPatterns("http://localhost:49322")要和Spring Security的CORS配置完全匹配,避免跨域预检失败导致的403。


二、STOMP协议后续请求的认证方案

根据你提到的两种业务场景,分别对应不同的实现方式:

场景1:握手阶段就携带JWT令牌(推荐方案)

你现在的客户端已经在握手时传递了Authorization头:

this.stompClient.connect({"Authorization" : `Bearer ${this.token}`}, () => { ... })

因为WebSocket握手是HTTP请求,你的JwtAuthFilter会自动处理这个请求头,验证JWT并将Authentication对象绑定到WebSocket会话中。后续所有通过该会话发送的STOMP消息,都会自动继承这个会话的Principal身份信息。

这种情况下,你只需要通过WebSocketSecurityConfigconfigureInbound方法配置消息权限即可(比如要求/app/**必须认证),不用再为每个消息单独验证令牌,@AuthenticationPrincipal UserDetails user可以直接在@MessageMapping方法中获取当前用户。

场景2:握手允许匿名,仅STOMP消息携带令牌

如果业务需要握手阶段匿名,仅在发送具体消息时携带令牌(比如你示例中的stompClient.sendAuthorization头),那你需要自定义Channel拦截器,从STOMP消息的请求头中提取JWT并验证,然后将认证信息绑定到当前消息上下文:

@Component
public class JwtStompInterceptor implements ChannelInterceptor {

    private final JwtAuthFilter jwtAuthFilter;
    private final AuthenticationManager authenticationManager;

    public JwtStompInterceptor(JwtAuthFilter jwtAuthFilter, AuthenticationManager authenticationManager) {
        this.jwtAuthFilter = jwtAuthFilter;
        this.authenticationManager = authenticationManager;
    }

    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        assert accessor != null;

        // 仅处理客户端发送消息的请求(对应stompClient.send方法)
        if (StompCommand.SEND.equals(accessor.getCommand())) {
            String authHeader = accessor.getFirstNativeHeader("Authorization");
            if (authHeader != null && authHeader.startsWith("Bearer ")) {
                String token = authHeader.substring(7);
                // 复用已有JWT过滤器的验证逻辑
                try {
                    UsernamePasswordAuthenticationToken authToken = jwtAuthFilter.getAuthentication(token);
                    Authentication authenticated = authenticationManager.authenticate(authToken);
                    // 将认证信息绑定到当前消息
                    accessor.setUser(authenticated);
                } catch (AuthenticationException e) {
                    throw new AccessDeniedException("无效的JWT令牌");
                }
            }
        }
        return message;
    }
}

然后将该拦截器配置到WebSocket的入站通道中,在WebSocketSecurityConfig中添加:

@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
    registration.interceptors(jwtStompInterceptor);
}

配置完成后,客户端每次发送消息时携带的Authorization头会被自动验证,@AuthenticationPrincipal依然可以正常获取用户信息。


三、你的疑问解答

  1. 为什么HttpSecurity的permitAll()没生效?
    Spring Security的WebSocket安全模块会对握手请求做独立检查,仅靠HttpSecurity的配置无法覆盖WebSocket的安全规则,必须配合SecurityWebSocketMessageBrokerConfigurer的配置才能生效。

  2. 握手带JWT后,后续STOMP消息为什么能自动认证?
    WebSocket会话与初始握手的HTTP会话绑定,握手时验证JWT后设置的Principal会被保存在WebSocket会话中,后续所有通过该会话发送的STOMP消息都会自动携带这个身份信息,无需重复验证。

  3. 要不要用AbstractSecurityWebSocketMessageBrokerConfigurer?
    该抽象类已被标记为Deprecated,直接实现SecurityWebSocketMessageBrokerConfigurer是更推荐的方式,功能完全一致。


备注:内容来源于stack exchange,提问作者Abas jama

火山引擎 最新活动