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:
重新配置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(); }添加CSRF Token获取端点
创建Controller返回当前CSRF Token:@RestController public class CsrfController { @GetMapping("/csrf") public CsrfToken getCsrfToken(HttpServletRequest request) { return (CsrfToken) request.getAttribute(CsrfToken.class.getName()); } }前端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
相关产品推荐
相关产品推荐

