React+ASP.NET Core WebSocket传Token握手失败问题求助
问题
我正在开发一个React应用,需与ASP.NET Core后端建立WebSocket连接实现文件上传功能。后端要求身份验证,我尝试通过react-use-websocket库的useWebSocket钩子将认证Token作为子协议传递,但连接失败;移除Token后连接正常。
前端代码
import React, { useState } from 'react'; import useWebSocket from 'react-use-websocket'; function App() { const socketUrl = "ws://localhost:5204/api/v1/upload/file?projectId=102"; const token = "my-token"; const { sendMessage, readyState } = useWebSocket(socketUrl, { protocols: ['realProtocol', token], }); // 剩余组件代码... }
报错信息
System.Net.WebSockets.WebSocketException: 'The remote party closed the WebSocket connection without completing the close handshake.'
后端代码
[Route("file")] [AllowAnonymous] public async Task<object> UploadListOfFiles(int projectId) { try { if (HttpContext.WebSockets.IsWebSocketRequest) { // 获取Sec-WebSocket-Protocol头的值 var protocolHeader = HttpContext.Request.Headers["Sec-WebSocket-Protocol"]; var token = protocolHeader.FirstOrDefault()?.Replace("realProtocol, ", ""); var authenticationResult = await _customJwtBearerHandler.HandleTokenAuthenticationAsync(token); if (authenticationResult.Succeeded) { var email = _customJwtBearerHandler.ExtractEmailFromToken(token).ToLower(); using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(); var processedFiles = await _fileUploadClient.ReceiveFileChunks(HttpContext, webSocket); List<FileUploadValidationResultModel> validationResults = await _validators.Validate(processedFiles, projectId); List<ApiResponse> responses = await _fileUploadClient.ValidateAndProcessUpload(email, validationResults, projectId); return responses; } else { return StatusCodes.Status401Unauthorized; } } else { return StatusCodes.Status400BadRequest; } } }
原因分析
WebSocket子协议的核心规则是:客户端发送的子协议列表中,服务器必须选择其中一个并返回给客户端,否则客户端会判定连接协商失败,主动关闭连接。
你的代码存在两个关键问题:
- 前端传递了
['realProtocol', token]两个子协议,但后端调用AcceptWebSocketAsync()时未指定要接受的子协议,ASP.NET Core默认返回空的子协议头,客户端因无匹配协议断开连接。 - 后端通过
Replace("realProtocol, ", "")提取Token的方式鲁棒性极差,一旦子协议顺序变化、格式调整(比如无空格),就会导致Token提取错误,触发认证失败。
解决方案
方案1:修正子协议处理逻辑(不推荐,仅适配当前写法)
前端调整
将Token嵌入自定义子协议中,避免传递多个子协议:
const { sendMessage, readyState } = useWebSocket(socketUrl, { protocols: [`realProtocol;token=${token}`], });
后端调整
解析自定义子协议提取Token,并确保返回客户端请求的子协议:
if (HttpContext.WebSockets.IsWebSocketRequest) { var protocolHeader = HttpContext.Request.Headers["Sec-WebSocket-Protocol"].FirstOrDefault(); string token = null; string selectedProtocol = null; if (!string.IsNullOrEmpty(protocolHeader)) { var parts = protocolHeader.Split(';'); selectedProtocol = parts[0]; // 遍历提取Token foreach (var part in parts) { if (part.Trim().StartsWith("token=")) { token = part.Substring("token=".Length).Trim(); break; } } } var authenticationResult = await _customJwtBearerHandler.HandleTokenAuthenticationAsync(token); if (authenticationResult.Succeeded) { // 必须返回客户端请求的子协议,完成协商 using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync(selectedProtocol); var email = _customJwtBearerHandler.ExtractEmailFromToken(token).ToLower(); var processedFiles = await _fileUploadClient.ReceiveFileChunks(HttpContext, webSocket); List<FileUploadValidationResultModel> validationResults = await _validators.Validate(processedFiles, projectId); List<ApiResponse> responses = await _fileUploadClient.ValidateAndProcessUpload(email, validationResults, projectId); return responses; } else { return StatusCodes.Status401Unauthorized; } }
方案2:标准Token传递方式(推荐)
子协议的设计初衷是定义WebSocket消息格式,并非传递认证信息。更合规的方式是通过请求头传递Token:
前端调整
通过headers配置Authorization头:
const { sendMessage, readyState } = useWebSocket(socketUrl, { protocols: ['realProtocol'], headers: { Authorization: `Bearer ${token}` } });
后端调整
从请求头提取Token,并指定接受的子协议:
if (HttpContext.WebSockets.IsWebSocketRequest) { // 从Authorization头提取Token var token = HttpContext.Request.Headers["Authorization"].FirstOrDefault()?.Replace("Bearer ", ""); var authenticationResult = await _customJwtBearerHandler.HandleTokenAuthenticationAsync(token); if (authenticationResult.Succeeded) { var email = _customJwtBearerHandler.ExtractEmailFromToken(token).ToLower(); // 指定接受的子协议,完成协商 using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync("realProtocol"); var processedFiles = await _fileUploadClient.ReceiveFileChunks(HttpContext, webSocket); List<FileUploadValidationResultModel> validationResults = await _validators.Validate(processedFiles, projectId); List<ApiResponse> responses = await _fileUploadClient.ValidateAndProcessUpload(email, validationResults, projectId); return responses; } else { return StatusCodes.Status401Unauthorized; } }
内容的提问来源于stack exchange,提问作者yatin tripathi
相关产品推荐
相关产品推荐

