使用WebSocket时AbpSession.UserId始终为null的原因排查
我正在使用ASP.NET Boilerplate框架,尝试在用户建立WebSocket连接时将连接与登录用户ID关联,但发现AbpSession.UserId的值始终为null,而该值在其他类中可正常获取。相关代码与配置如下:
服务端代码
public class WebSocketServerConnectionManager : ITransientDependency { private ConcurrentDictionary<string, WebSocket> _socket = new ConcurrentDictionary<string, WebSocket>(); public IAbpSession AbpSession { get; set; } public WebSocketServerConnectionManager() { AbpSession = NullAbpSession.Instance; } public ConcurrentDictionary<string, WebSocket> GetAllSockets() { return _socket; } public string AddSocket(WebSocket socket) { try { var ConnId = AbpSession.UserId.ToString(); //<==== Null // Guid.NewGuid().ToString(); //var test = AbpSession.UserId; _socket.TryAdd(ConnId/*dictionary item key*/, socket/*dictionary item value*/); Debug.WriteLine("Connection Added:" + ConnId); return ConnId; } catch(Exception ex) { throw new Exception(ex.Message); } } }
客户端WebSocket初始化代码
async ngOnInit() { this.connectWebSocket(); } connectWebSocket(){ this.socket=new WebSocket(this.webSocketUrl); this.socket.onopen=(event)=>{}; this.socket.onclose=(event)=>{}; this.socket.onmessage=(event)=>{ this.isConnID(event.data); }; }
AuthConfigurer中的代码
/* This method is needed to authorize SignalR javascript client. * SignalR can not send authorization header. So, we are getting it from query string as an encrypted text. */ private static Task QueryStringTokenResolver(MessageReceivedContext context) { var test = context.HttpContext.Request.Path.Value;//the value is null if (!context.HttpContext.Request.Path.HasValue || (!context.HttpContext.Request.Path.Value.StartsWith("/signalr") && !context.HttpContext.Request.Path.Value.StartsWith("/ws"))) { // We are just looking for signalr clients return Task.CompletedTask; } var qsAuthToken = context.HttpContext.Request.Query["enc_auth_token"].FirstOrDefault(); if (qsAuthToken == null) { // Cookie value does not matches to querystring value return Task.CompletedTask; } // Set auth token from cookie context.Token = SimpleStringCipher.Instance.Decrypt(qsAuthToken, AppConsts.DefaultPassPhrase); return Task.CompletedTask; }
appsettings.json的Authentication配置
"Authentication": { "JwtBearer": { "IsEnabled": "true", "SecurityKey": "Test_C421AAEE0D114E9C", "Issuer": "Test", "Audience": "Test" } },
原因分析
WebSocket连接未携带认证Token
客户端初始化WebSocket时,this.webSocketUrl没有附加enc_auth_token参数。服务端的QueryStringTokenResolver依赖该参数获取加密的JWT Token,缺少这个参数就无法完成认证,自然无法通过AbpSession获取用户ID。认证路径匹配逻辑失效
代码中context.HttpContext.Request.Path.Value为null,导致路径判断条件!context.HttpContext.Request.Path.HasValue成立,直接跳过了Token解析逻辑。即使WebSocket路径是/ws或/signalr,也不会执行认证流程,无法建立用户会话。WebSocket处理管道未集成ABP会话上下文
WebSocket连接的处理流程可能没有经过ABP的核心中间件(如UseAbp()),导致AbpSession无法从当前请求上下文获取已认证的用户信息。ABP的会话依赖请求上下文的认证结果,如果WebSocket管道未正确集成ABP中间件,AbpSession会默认使用NullAbpSession.Instance,返回null。AbpSession注入时机问题
虽然WebSocketServerConnectionManager实现了ITransientDependency,但如果WebSocket连接的处理是在ABP请求上下文之外进行的(比如直接在底层WebSocket中间件中调用),AbpSession可能没有被正确注入当前用户的会话信息,依然使用默认的NullAbpSession。
内容的提问来源于stack exchange,提问作者Abdulaziz Burghal

