.NET Core 3.0 SignalR连接时UserIdentifier及User.Identity.Name为null
你遇到的Context.User.Identity.Name和UserIdentifier为null的问题,核心原因是你的.NET Core服务端没有配置身份认证中间件。SignalR本身不负责用户身份的识别,它依赖于.NET Core的认证系统来获取用户信息——如果没有配置认证,服务端就无法识别当前连接的用户,自然这些属性都是null。
下面是具体的解决步骤:
1. 配置服务端身份认证
首先需要在你的.NET Core 3.0项目中添加并启用身份认证中间件,根据你的客户端类型(Angular是SPA,推荐用JWT;如果是传统Web可以用Cookie)选择合适的认证方式:
方式一:Cookie认证(适合简单场景)
在Startup.cs的ConfigureServices方法中添加:
using Microsoft.AspNetCore.Authentication.Cookies; public void ConfigureServices(IServiceCollection services) { // 其他服务配置... services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(options => { options.LoginPath = "/Account/Login"; // 替换为你的登录页面路径 }); services.AddSignalR(); }
然后在Configure方法中,必须保证中间件顺序正确——UseAuthentication要放在UseRouting之后,UseEndpoints之前:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // 其他中间件(比如UseStaticFiles、UseHttpsRedirection)... app.UseRouting(); // 启用认证和授权 app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapHub<MessageHub>("/message"); // 映射SignalR Hub // 其他路由配置... }); }
方式二:JWT认证(适合Angular这类SPA)
如果你的Angular客户端用JWT做身份验证,需要在服务端配置JWT支持:
在Startup.cs的ConfigureServices中添加:
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.Text; public void ConfigureServices(IServiceCollection services) { // 其他服务配置... services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Jwt:Issuer"], // 从appsettings.json读取配置 ValidAudience = Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])) }; // 为SignalR配置JWT解析:从查询字符串或请求头获取token options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; var path = context.HttpContext.Request.Path; // 当请求SignalR Hub时,从查询字符串获取token if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/message")) { context.Token = accessToken; } return Task.CompletedTask; } }; }); services.AddSignalR(); }
对应的Angular客户端连接时,需要带上JWT token:
// 从本地存储获取已登录用户的JWT token const jwtToken = localStorage.getItem('auth_token'); public startConnection = () => { this.hubConnection = new signalR.HubConnectionBuilder() .withUrl('https://localhost:44363/message',{ skipNegotiation: true, transport: signalR.HttpTransportType.WebSockets, accessTokenFactory: () => jwtToken // 传递token到服务端 }) .build(); // 后续连接逻辑... }
2. 自定义UserIdentifier(可选)
Context.UserIdentifier默认等于Context.User.Identity.Name,如果你想用用户ID或其他标识作为UserIdentifier,可以自定义IUserIdProvider:
- 创建自定义Provider类:
using Microsoft.AspNetCore.SignalR; using System.Security.Claims; public class CustomUserIdProvider : IUserIdProvider { public string GetUserId(HubConnectionContext connection) { // 这里返回用户的唯一标识,比如从Claim中获取用户ID return connection.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; } }
- 在
Startup.cs的ConfigureServices中注册这个服务:
services.AddSingleton<IUserIdProvider, CustomUserIdProvider>();
3. 验证身份是否生效
你可以在MessageHub的OnConnectedAsync方法中添加日志,确认用户是否已认证:
public override Task OnConnectedAsync() { Console.WriteLine($"用户是否已认证: {Context.User.Identity.IsAuthenticated}"); if (Context.User.Identity.IsAuthenticated) { string name = Context.User.Identity.Name; MyUsers.TryAdd(Context.ConnectionId, name); Console.WriteLine($"已添加用户: {name},连接ID: {Context.ConnectionId}"); } return base.OnConnectedAsync(); }
4. 修正私有消息发送逻辑
当用户身份正确后,Clients.Users(user)就能正确定位到目标用户了——这里的user参数需要是目标用户的UserIdentifier(或Identity.Name,取决于你的配置)。
内容的提问来源于stack exchange,提问作者chink

