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

Spring Boot+React WebSocket在Firefox正常,Chrome中连接立即关闭

问题原因分析

Chrome 对 WebSocket 握手阶段的 Sec-WebSocket-Protocol 头处理更为严格:当客户端发送多个子协议(你通过数组 ["Authorization", accessToken] 传递,最终头值为 Authorization,<token>)时,Chrome 要求服务端必须返回其中一个合法的子协议,否则会判定握手失败并立即关闭连接。而 Firefox 和 Insomnia 对该规则的校验更宽松,允许服务端不返回该头仍维持连接。

另外你提到服务端因客户端未发消息主动关闭连接,本质也是握手失败导致连接未正常建立,客户端无法发送消息。

解决方法

1. 调整前端协议头传递方式

将认证信息合并为单个字符串传入 protocols,避免 Chrome 识别为多个子协议:

const {lastMessage, readyState} = useWebSocket(socketUrl, {
            protocols: [`Authorization, ${accessToken}`], // 合并为单个协议字符串
            share: true,
            retryOnError: true,
            shouldReconnect: (e) => true,
            reconnectInterval: 4,
            reconnectAttempts: 2,
            onError: (e) => {
                console.error('Error in websocket', e)
            }
        },
        Boolean(accessToken)
    );

2. 修改 Spring Boot 服务端握手逻辑

添加握手拦截器,提取认证信息后,将相同的 Sec-WebSocket-Protocol 头返回给客户端,满足 Chrome 的握手校验要求:

实现握手拦截器

@Component
public class WebSocketAuthInterceptor implements HandshakeInterceptor {
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        List<String> protocolHeaders = request.getHeaders().get("Sec-WebSocket-Protocol");
        if (protocolHeaders != null && !protocolHeaders.isEmpty()) {
            String authProtocol = protocolHeaders.get(0);
            // 解析令牌:分割出Authorization后的token部分
            String token = authProtocol.replace("Authorization, ", "").trim();
            
            // 这里加入你的令牌校验逻辑,比如解析JWT、验证用户身份
            // 验证通过后可将用户信息存入attributes,供后续WebSocket处理器使用
            // attributes.put("user", authenticatedUser);
            
            // 返回相同的协议头给客户端,满足Chrome的握手要求
            response.getHeaders().add("Sec-WebSocket-Protocol", authProtocol);
        }
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
        // 无需额外处理
    }
}

注册拦截器到 WebSocket 配置

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    private final WebSocketAuthInterceptor authInterceptor;

    public WebSocketConfig(WebSocketAuthInterceptor authInterceptor) {
        this.authInterceptor = authInterceptor;
    }

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(yourWebSocketHandler(), "/your-websocket-endpoint")
                .addInterceptors(authInterceptor)
                .setAllowedOrigins("*"); // 根据实际跨域需求调整
    }

    @Bean
    public WebSocketHandler yourWebSocketHandler() {
        return new YourCustomWebSocketHandler(); // 替换为你的WebSocket处理器
    }
}

3. 可选:调整服务端连接超时设置

如果服务端仍存在因无消息关闭连接的情况,可在 WebSocket 配置中增加心跳或超时配置,比如通过 @ServerEndpoint 的 session.setMaxIdleTimeout() 方法延长空闲超时时间。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 23:03:27