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

Spring Boot 3 + Spring Security 6 WebSocket认证问题求助

Spring Boot 3 + Spring Security 6 WebSocket 认证问题解决方案

核心问题定位

  1. 添加@EnableWebSocketSecurity后出现access denied:该注解会启用Spring Security对WebSocket消息的严格认证控制,默认拒绝所有未认证消息,但未正确配置认证规则与上下文关联导致权限拦截。
  2. CSRF配置后出现请求对象回收错误:WebSocket握手完成后,原始HTTP请求对象会被容器回收,若在消息处理阶段尝试获取该对象就会触发此错误。

分步解决方案

1. 正确配置WebSocket Security规则

使用@EnableWebSocketSecurity时,必须显式定义消息的认证规则,同时确保WebSocket认证与HTTP认证上下文共享:

@Configuration
@EnableWebSocket
@EnableWebSocketSecurity
public class WebSocketsConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(customWebSocketHandler(), "/ws/connect")
                .setAllowedOrigins("https://your-frontend-domain.com") // 替换为实际跨域域名
                .withSockJS(); // 若使用SockJS则保留,否则移除
    }

    @Bean
    public WebSocketHandler customWebSocketHandler() {
        return new CustomWebSocketHandler();
    }

    // 配置WebSocket消息认证规则
    @Bean
    public SecurityWebSocketFilterChain webSocketSecurityFilterChain(WebSocketSecurity webSocket) {
        webSocket
                .authorizeMessages(messages -> messages
                        .anyMessage().authenticated() // 所有消息需已认证用户发送
                )
                .csrf(csrf -> csrf
                        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // 允许前端从Cookie读取CSRF Token
                );
        return webSocket.build();
    }
}

2. 修正HTTP Security配置,适配WebSocket握手

确保WebSocket握手端点允许匿名访问(后续消息认证由WebSocket Security控制),同时配置CSRF Token的存储方式:

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/ws/connect/**").permitAll() // 允许WebSocket握手请求匿名访问
                        .anyRequest().authenticated()
                )
                .csrf(csrf -> csrf
                        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // 统一CSRF Token存储
                )
                // 根据你的认证方式添加配置(如JWT、Session)
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED));

        return http.build();
    }
}

3. 前端正确传递CSRF Token到握手请求

WebSocket握手是HTTP POST请求,需将CSRF Token放在请求头中,而非WebSocket消息帧里:

import SockJS from 'sockjs-client';
import Stomp from 'stompjs';

// 从Cookie中获取Spring Security生成的XSRF-TOKEN
const getCsrfToken = () => {
    const match = document.cookie.match(/XSRF-TOKEN=([^;]+)/);
    return match ? match[1] : '';
};

export const initWebSocket = () => {
    const socket = new SockJS('/ws/connect');
    const stompClient = Stomp.over(socket);

    // 握手时添加CSRF请求头
    stompClient.connect(
        { 'X-XSRF-TOKEN': getCsrfToken() },
        (frame) => {
            console.log('WebSocket连接成功:', frame);
            stompClient.subscribe('/topic/notify', (msg) => {
                // 处理收到的消息
                console.log('收到消息:', JSON.parse(msg.body));
            });
        },
        (error) => {
            console.error('WebSocket连接失败:', error);
        }
    );

    return stompClient;
};

4. 避免在消息处理阶段访问已回收的HTTP请求对象

不要在WebSocket消息处理逻辑中尝试获取HttpServletRequest,应从WebSocketSession的属性中获取认证信息:

public class CustomWebSocketHandler extends TextWebSocketHandler {

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        // 从Session属性中获取认证对象(由Spring Security自动注入)
        Authentication auth = (Authentication) session.getAttributes().get(WebSocketSecurityConstants.AUTHENTICATION_ATTR_NAME);
        
        if (auth == null) {
            session.close(CloseStatus.NOT_ACCEPTABLE);
            return;
        }

        // 使用认证信息处理业务逻辑
        String username = auth.getName();
        System.out.printf("用户[%s]发送消息: %s%n", username, message.getPayload());
        
        // 回复消息示例
        session.sendMessage(new TextMessage("已收到你的消息: " + message.getPayload()));
    }
}

关键注意事项

  • @EnableWebSocketSecurity会完全接管WebSocket的安全控制,必须显式配置authorizeMessages,否则默认拒绝所有消息。
  • 若使用JWT认证,需额外配置WebSocketAuthenticationManager,将握手请求头中的JWT Token解析为认证对象。
  • 跨域配置需严格匹配前端域名,避免使用*(生产环境禁止)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 22:27:23