SpringBoot3添加自定义Filter后WebSocket连接报401错误求助
服务在SpringBoot 2.6(内置SpringFramework 5)下运行正常,但升级到SpringBoot 3.2.4(内置SpringFramework 6)后,WebSocket服务器无法连接,客户端提示:Error: Unexpected server response: 401。
排查过程:搭建简单测试项目逐步验证,创建WebSocket配置与处理器、多端口配置、WebSocket拦截器与HTTP过滤器均正常,直到添加自定义Filter注册配置后,WebSocket连接开始报401错误。
相关代码
1. SecurityConfig
@Configuration public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests((requests) -> requests .anyRequest().permitAll() ).httpBasic(withDefaults()); return http.build(); } }
2. WebSocketConfig
@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(testingHandler(), "/ws/{id}").setAllowedOrigins("*") .addInterceptors(handshakeInterceptor()); } @Bean public WebSocketHandler testingHandler() { return new TestingHandler(); } @Bean public HandshakeInterceptor handshakeInterceptor() { return new TestingInterceptor(); } @Bean public ServletServerContainerFactoryBean createWebSocketContainer() { ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); container.setMaxTextMessageBufferSize(WEB_SOCKET_BUFFER_SIZE); container.setMaxBinaryMessageBufferSize(WEB_SOCKET_BUFFER_SIZE); return container; } }
3. ServerConfig(多端口配置)
@Configuration public class ServerConfig { @Value("${server.additionalPorts}") private String additionalPorts; /** * 多端口配置 */ @Bean public TomcatServletWebServerFactory servletContainer() { TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); Connector[] additionalConnectors = this.additionalConnector(); if (additionalConnectors != null && additionalConnectors.length > 0) { tomcat.addAdditionalTomcatConnectors(additionalConnectors); } //Invalid character error tomcat.addConnectorCustomizers(new TomcatConnectorCustomizer() { @Override public void customize(Connector connector) { connector.setProperty("relaxedQueryChars", "|{}[]"); } }); return tomcat; } private Connector[] additionalConnector() { if (!StringUtils.hasLength(this.additionalPorts)) { return null; } String[] ports = this.additionalPorts.split(","); List<Connector> result = new ArrayList<>(); for (String port : ports) { Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); connector.setScheme("http"); connector.setProperty("relaxedQueryChars", "|{}[]"); connector.setPort(Integer.valueOf(port)); result.add(connector); } return result.toArray(new Connector[] {}); } }
4. WebMvcConfig(Filter注册)
@Configuration public class WebMvcConfig{ @Bean public Filter testingFilter(){ return new TestingFilter(); } @Bean public FilterRegistrationBean<DelegatingFilterProxy> filterRegist() { FilterRegistrationBean<DelegatingFilterProxy> registration = new FilterRegistrationBean(); registration.setFilter(new DelegatingFilterProxy("testingFilter")); registration.addUrlPatterns("/rest/*");//URI路径过滤 registration.setOrder(1); return registration; } }
(注:Filter、Interceptor、WebSocket处理器均为仅含空方法的测试代码)
问题原因与解决方案
核心原因
SpringBoot 3.x(基于Spring Framework 6)中,FilterRegistrationBean的asyncSupported默认值从true改为false。WebSocket握手依赖HTTP Upgrade异步请求,不支持异步的Filter会阻断握手流程,最终返回401错误。同时你使用DelegatingFilterProxy的注册方式存在冗余,未明确开启异步支持,进一步触发了该问题。
解决方案
方案一:修改现有Filter注册,添加异步支持
调整WebMvcConfig中的filterRegist方法,增加异步支持配置:
@Bean public FilterRegistrationBean<DelegatingFilterProxy> filterRegist() { FilterRegistrationBean<DelegatingFilterProxy> registration = new FilterRegistrationBean(); registration.setFilter(new DelegatingFilterProxy("testingFilter")); registration.addUrlPatterns("/rest/*"); registration.setOrder(1); registration.setAsyncSupported(true); // 开启异步支持 return registration; }
方案二:简化Filter注册(推荐)
直接注册TestingFilter的FilterRegistrationBean,无需使用DelegatingFilterProxy,同时开启异步支持:
@Configuration public class WebMvcConfig{ @Bean public TestingFilter testingFilter(){ return new TestingFilter(); } @Bean public FilterRegistrationBean<TestingFilter> filterRegist(TestingFilter testingFilter) { FilterRegistrationBean<TestingFilter> registration = new FilterRegistrationBean<>(testingFilter); registration.addUrlPatterns("/rest/*"); registration.setOrder(1); registration.setAsyncSupported(true); return registration; } }
补充说明
WebSocket握手请求属于异步处理场景,所有可能拦截该请求路径的Filter都必须开启异步支持(asyncSupported=true),否则会导致握手失败。SpringBoot 2.x与3.x在此配置上的默认值差异是引发兼容性问题的关键。
内容的提问来源于stack exchange,提问作者iyaya lc

