SignalR Core按用户名发消息异常:Context.User.Identity.Name始终为空
咱们一步步拆解问题,找到核心原因并逐个解决:
1. 修复JWT Claim与Identity.Name的映射
Context.User.Identity.Name默认会读取ClaimTypes.Name类型的Claim,但你生成Token时并未添加该Claim——虽然你配置了UniqueName、NameId等字段,但ASP.NET Core不会自动将这些映射到Identity.Name。
修改你的Token生成方法,补充ClaimTypes.Name的声明:
private TokenResponse generateToken(User user) { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes(_jwtSettings.Secret); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new System.Security.Claims.ClaimsIdentity(new[] { new Claim(JwtRegisteredClaimNames.Aud, user.Email), new Claim(JwtRegisteredClaimNames.Sub, user.Email), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Email, user.Email), new Claim(JwtRegisteredClaimNames.UniqueName, user.UserName), new Claim(JwtRegisteredClaimNames.NameId, user.UserName), new Claim(ClaimTypes.NameIdentifier,user.UserName), // 新增这一行,将用户名映射到Identity.Name new Claim(ClaimTypes.Name, user.UserName) }), Expires = DateTime.UtcNow.AddMinutes(10), SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256) }; var token = tokenHandler.CreateToken(tokenDescriptor); TokenResponse response = new TokenResponse(); response.token = tokenHandler.WriteToken(token); return response; }
2. 修正UserIdProvider的类名拼写错误
你定义的UserIdProvider类是CustomUserIdProvider,但在Startup注册时写的是CustomUserProvider(少了Id后缀),这会导致SignalR无法加载正确的用户ID解析器:
// 原错误代码 services.AddSingleton<IUserIdProvider, CustomUserProvider>(); // 修改为正确类名 services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
3. 调整中间件执行顺序(关键!)
中间件顺序直接影响身份验证和跨域逻辑的有效性,你的当前配置存在两处问题:
UseCors必须放在路由和身份验证之前,否则跨域请求的身份信息无法被正确识别- ASP.NET Core 3.x推荐使用
UseEndpoints统一映射路由和SignalR Hub,避免UseSignalR和UseMvc的冲突
修改Configure方法:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); // 跨域中间件放在最前面,确保所有请求都能处理跨域逻辑 app.UseCors(x => x .AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader()); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); // 统一用Endpoints映射控制器和SignalR Hub app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapHub<NotificationHub>("/myHub"); }); // 移除UseSignalR和UseMvc,避免路由冲突 // app.UseSignalR(routes => { routes.MapHub<NotificationHub>("/myHub"); }); // app.UseMvc(routes => { ... }); }
4. 规范JWT验证参数配置
你的JWT验证参数同时设置了ValidateAudience = false和ValidAudience,这会导致验证逻辑混乱。如果不需要验证Audience,直接移除ValidAudience配置;如果需要验证,则将ValidateAudience设为true并确保Token中的Aud字段与配置一致:
x.TokenValidationParameters = new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtSettings.Secret)), ValidateIssuer = false, ValidateAudience = false, // 移除冲突的配置项 // ValidIssuer = Configuration["JwtSettings:Issuer"], // ValidAudience = Configuration["JwtSettings:Issuer"], RequireExpirationTime = true, // 开启过期时间验证,提升安全性 ClockSkew = TimeSpan.FromMinutes(60), ValidateLifetime = true, };
5. 客户端Token传递验证
确保Android客户端连接SignalR时,正确将JWT Token作为access_token参数附加到连接URL中,示例格式:wss://your-domain.com/myHub?access_token=your-jwt-token-string
完成以上所有步骤后,重新生成Token并测试,Hub中的Context.User.Identity.Name就能正确获取到用户名,Clients.User(name).SendAsync也可以正常按用户名发送消息了。
内容的提问来源于stack exchange,提问作者Haytham

