Blazor Server中为客户端配置服务端SignalR身份标识(JWT/Cookie)
问题说明
已实现JWT认证,但登录后SignalR服务端无法识别客户端Identity;使用Cookie认证时,创建HubConnectionBuilder后服务端同样无法识别客户端身份。已配置SignalR通过用户ID(邮箱/名称)识别用户,自定义了IUserIdProvider实现,且Hub已添加[Authorize]特性。
自定义IUserIdProvider代码:
public class CustomEmailProvider : IUserIdProvider { public virtual string GetUserId(HubConnectionContext connection) { var res = connection.User?.Claims.FirstOrDefault(x=>x.Type==ClaimTypes.NameIdentifier && x.Value == connection?.User?.Identity?.Name); return res?.Value; } }
解决方案
一、JWT认证场景修复
SignalR的WebSocket连接无法自动携带Authorization请求头,需要在服务端配置JwtBearer支持从查询参数提取Token,同时客户端手动传入Token。
1. 服务端Program.cs配置修改
builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; }).AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters() { ValidateAudience=true, ValidateIssuer=true, ValidateLifetime=true, ValidateIssuerSigningKey=true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience=builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(builder.Configuration["Jwt:Key"])), }; // 新增:适配SignalR的WebSocket连接,从查询参数提取Token options.Events = new JwtBearerEvents { OnMessageReceived = context => { var accessToken = context.Request.Query["access_token"]; var path = context.HttpContext.Request.Path; if (!string.IsNullOrEmpty(accessToken) && path.StartsWithSegments("/ConnectionsHub")) { context.Token = accessToken; } return Task.CompletedTask; } }; });
2. 客户端Hub连接代码修改
protected override async Task OnInitializedAsync() { if (hub is null) { // 从本地存储获取已登录的JWT Token(根据实际存储方式调整) var token = await _localStorage.GetItemAsync<string>("authToken"); hub = new HubConnectionBuilder() .WithUrl(_NavigationManager.ToAbsoluteUri("/ConnectionsHub"), options => { // 配置Token提供者,自动将Token加入查询参数 options.AccessTokenProvider = () => Task.FromResult(token); }) .Build(); } // 启动Hub连接 await hub.StartAsync(); }
二、Cookie认证场景修复
Cookie认证下需确保服务端CORS允许携带凭证,客户端连接时明确包含Cookie。
1. 服务端CORS配置修改
builder.Services.AddCors(options => options.AddDefaultPolicy(builder => builder.AllowAnyHeader() .AllowAnyMethod() .AllowCredentials() // 必须开启,允许携带Cookie .SetIsOriginAllowed(origin => true) // 替代AllowAnyOrigin,避免和AllowCredentials冲突 ));
2. 客户端Hub连接代码修改
protected override async Task OnInitializedAsync() { if (hub is null) { hub = new HubConnectionBuilder() .WithUrl(_NavigationManager.ToAbsoluteUri("/ConnectionsHub"), options => { // 明确允许携带Cookie凭证 options.Credentials = HttpCredentialsInclude.Include; }) .Build(); } await hub.StartAsync(); }
三、优化自定义IUserIdProvider
当前实现逻辑冗余,可简化为直接获取Identity的Name(确保认证时该声明已正确包含):
public class CustomEmailProvider : IUserIdProvider { public string GetUserId(HubConnectionContext connection) { // 直接返回已认证用户的Identity名称(邮箱/用户名) return connection.User?.Identity?.Name; } }
四、额外检查点
- 生成JWT Token时,确保包含
ClaimTypes.Name或ClaimTypes.NameIdentifier声明,示例:var claims = new List<Claim> { new Claim(ClaimTypes.Name, user.Email), // 其他必要声明 }; - 保持服务端中间件顺序正确:
UseRouting→UseCors→UseAuthentication→UseAuthorization→UseEndpoints - 在Hub方法中添加调试代码,检查身份解析情况:
public async Task TestAuth() { Console.WriteLine($"IsAuthenticated: {Context.User.Identity.IsAuthenticated}"); foreach(var claim in Context.User.Claims) { Console.WriteLine($"{claim.Type}: {claim.Value}"); } }
内容的提问来源于stack exchange,提问作者PontiacGTX
相关产品推荐
相关产品推荐

