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

Spring WebSocket CSRF禁用失败及Token获取方法求助

问题描述

我正在开发一款由Spring Security处理HTTP接口认证的应用,已禁用HTTP的CSRF保护,现希望禁用Spring WebSocket的CSRF保护,但尝试多种方法均未成功。若无法禁用该保护,请问如何获取CSRF Token?(已尝试配置CSRF端点获取Token但无效,且找到的教程均已过时)

现有配置

WebSocket安全配置

@Configuration
@EnableWebSocketSecurity
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
    @Bean
    AuthorizationManager<Message<?>> messageAuthorizationManager(
            MessageMatcherDelegatingAuthorizationManager.Builder messages) {
        messages.anyMessage().permitAll();

        return messages.build();
    }

    @Override
    protected boolean sameOriginDisabled() {
        return true;
    }
}

HTTP安全配置

@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig {

    @Autowired
    private JwtFilter jwtFilter;

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.addFilterBefore(jwtFilter, BasicAuthenticationFilter.class)
                .cors(AbstractHttpConfigurer::disable)
                .csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/authenticate").permitAll()
                        .requestMatchers("/createchatroom").authenticated()
                        .requestMatchers("/public/*").permitAll()
                        .requestMatchers("/private/*").permitAll()
                        .requestMatchers("/ws/**").authenticated()
                        .requestMatchers("/register").permitAll()
                        .requestMatchers("/csrf").authenticated()
                        .requestMatchers("/addEmployeeToFavorites").hasAnyAuthority(EMPLOYEE.name(),
                                ADMIN.name())
                        .requestMatchers("/addChatRoomToFavorites")
                                .hasAnyAuthority(EMPLOYEE.name(), ADMIN.name())
                        .requestMatchers("/home").hasAnyAuthority(EMPLOYEE.name(), ADMIN.name()))
                .build();
    }
}
解决方案

方法一:彻底禁用WebSocket的CSRF保护

你当前继承的AbstractSecurityWebSocketMessageBrokerConfigurer已被标记为过时,推荐改用SecurityWebSocketMessageBrokerConfigurer接口,通过配置NullCsrfTokenRepository来完全禁用WebSocket的CSRF校验:

@Configuration
@EnableWebSocketSecurity
public class WebSocketSecurityConfig implements SecurityWebSocketMessageBrokerConfigurer {

    @Bean
    AuthorizationManager<Message<?>> messageAuthorizationManager(
            MessageMatcherDelegatingAuthorizationManager.Builder messages) {
        messages.anyMessage().permitAll();
        return messages.build();
    }

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

    @Bean
    public ChannelInterceptor csrfChannelInterceptor() {
        CsrfChannelInterceptor interceptor = new CsrfChannelInterceptor();
        // 配置空的CSRF Token仓库,直接禁用校验
        interceptor.setCsrfTokenRepository(new NullCsrfTokenRepository());
        return interceptor;
    }

    @Override
    public boolean sameOriginDisabled() {
        return true;
    }
}

同时可以在配置文件中添加全局开关确保生效:

spring.security.websocket.csrf.enabled=false

方法二:获取并携带CSRF Token(禁用失败时)

如果无法禁用WebSocket CSRF保护,按以下步骤获取并在握手时携带Token:

  1. 重新配置HTTP安全中的CSRF规则
    修改SecurityConfig,启用CSRF并配置Cookie存储,同时开放/csrf端点的匿名访问:

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http.addFilterBefore(jwtFilter, BasicAuthenticationFilter.class)
                .cors(AbstractHttpConfigurer::disable)
                .csrf(csrf -> csrf
                        // 配置Cookie存储Token,允许前端读取
                        .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
                        // 忽略不需要CSRF校验的接口
                        .ignoringRequestMatchers("/authenticate", "/register", "/public/**")
                )
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers("/authenticate").permitAll()
                        .requestMatchers("/createchatroom").authenticated()
                        .requestMatchers("/public/*").permitAll()
                        .requestMatchers("/private/*").permitAll()
                        .requestMatchers("/ws/**").authenticated()
                        .requestMatchers("/register").permitAll()
                        .requestMatchers("/csrf").permitAll()
                        .requestMatchers("/addEmployeeToFavorites").hasAnyAuthority(EMPLOYEE.name(), ADMIN.name())
                        .requestMatchers("/addChatRoomToFavorites").hasAnyAuthority(EMPLOYEE.name(), ADMIN.name())
                        .requestMatchers("/home").hasAnyAuthority(EMPLOYEE.name(), ADMIN.name()))
                .build();
    }
    
  2. 添加CSRF Token获取端点
    创建Controller返回当前CSRF Token:

    @RestController
    public class CsrfController {
        @GetMapping("/csrf")
        public CsrfToken getCsrfToken(HttpServletRequest request) {
            return (CsrfToken) request.getAttribute(CsrfToken.class.getName());
        }
    }
    
  3. 前端WebSocket握手时携带Token
    前端先请求/csrf获取Token,再在WebSocket握手的请求头中添加X-XSRF-TOKEN:

    async function connectWebSocket() {
        const csrfRes = await fetch('/csrf');
        const csrfToken = await csrfRes.json();
        
        // 原生WebSocket连接示例
        const socket = new WebSocket('ws://localhost:8080/ws', {
            headers: {
                'X-XSRF-TOKEN': csrfToken.token
            }
        });
    
        socket.onopen = () => console.log('WebSocket连接成功');
    }
    

    若使用SockJS,需将Token放在查询参数或通过setHeader方法设置,因为SockJS握手基于HTTP请求。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 11:40:20