Spring Boot 3.0.6自定义AuthorizationChannelInterceptor覆盖失败求助
Spring Boot 3.0.6 自定义WebSocket安全拦截器失败问题解决
核心问题原因
Spring Security 6.x(对应Spring Boot 3.x)中,WebSocket安全默认由AuthorizationChannelInterceptor处理,该拦截器优先级高于自定义拦截器,且若未正确将HTTP握手阶段的认证信息传递到WebSocket消息通道,就会导致后续处理时Authentication始终处于匿名状态,触发AccessDeniedException。
常见配置错误及修正方案
1. 未正确替换默认拦截器
Spring Boot 3.x中不能直接添加自定义拦截器覆盖默认逻辑,需在WebSocketMessageBrokerConfigurer中先移除默认拦截器,再将自定义拦截器设为最高优先级:
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { private final CustomAuthChannelInterceptor customAuthChannelInterceptor; public WebSocketConfig(CustomAuthChannelInterceptor customAuthChannelInterceptor) { this.customAuthChannelInterceptor = customAuthChannelInterceptor; } @Override public void configureClientInboundChannel(ChannelRegistration registration) { // 移除默认授权拦截器,添加自定义拦截器并设为最高优先级 registration.setInterceptors(customAuthChannelInterceptor) .order(Ordered.HIGHEST_PRECEDENCE); } // 其他WebSocket配置(如消息代理、端点映射)... }
2. 自定义拦截器未正确处理认证信息
自定义拦截器需在WebSocket CONNECT阶段从握手请求中提取认证凭证(如Token),生成Authentication并设置到上下文,确保后续流程能获取到认证信息:
@Component public class CustomAuthChannelInterceptor implements ChannelInterceptor { private final AuthenticationManager authenticationManager; public CustomAuthChannelInterceptor(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; } @Override public Message<?> preSend(Message<?> message, MessageChannel channel) { StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class); if (StompCommand.CONNECT.equals(accessor.getCommand())) { // 从请求头提取Token(示例为Bearer Token) String token = accessor.getFirstNativeHeader("Authorization"); if (token != null && token.startsWith("Bearer ")) { token = token.substring(7); // 自定义Token认证逻辑,生成Authentication UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(null, token); Authentication authentication = authenticationManager.authenticate(authRequest); // 将认证信息存入上下文及StompHeaderAccessor SecurityContextHolder.getContext().setAuthentication(authentication); accessor.setUser(authentication); } } return message; } }
3. SecurityConfig未放行WebSocket握手端点
需确保Spring Security配置中放行WebSocket握手端点,避免HTTP握手阶段被拦截导致认证信息丢失:
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // 放行WebSocket握手端点(根据实际路径调整) .requestMatchers("/ws/**").permitAll() .anyRequest().authenticated() ) // 若使用Token认证,需禁用CSRF(WebSocket握手一般不依赖Cookie+CSRF) .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); return http.build(); } // 自定义AuthenticationManager、UserDetailsService等Bean... }
4. 未配置SecurityContext线程传播
WebSocket消息线程默认不会继承HTTP请求的SecurityContext,需设置线程上下文继承策略,确保认证信息能传递到WebSocket处理线程:
@SpringBootApplication public class Application { public static void main(String[] args) { // 设置SecurityContext可被子线程继承 SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); SpringApplication.run(Application.class, args); } }
关键注意事项
- 必须在CONNECT阶段完成认证,SUBSCRIBE等后续命令会依赖此时设置的
Authentication; - 若未移除默认
AuthorizationChannelInterceptor,它会优先执行,此时认证信息尚未设置,必然触发权限异常; - 确保握手请求正确携带认证凭证(如Token),且自定义拦截器的认证逻辑与系统现有认证机制一致。
内容的提问来源于stack exchange,提问作者Pioter88
相关产品推荐
相关产品推荐

