如何用JwtBearer认证SignalR客户端并接入Cookie认证的同一Hub?
1. 完善JWT Bearer认证配置
先在Program.cs里补全JWT的配置,适配SignalR的WebSocket传输特性(控制台客户端通常会把token放在查询参数中):
// 从配置读取JWT参数(建议存放在appsettings.json) var jwtKey = builder.Configuration["Jwt:Key"] ?? "your_strong_secret_key_here"; var jwtIssuer = builder.Configuration["Jwt:Issuer"] ?? "your_app_issuer"; var jwtAudience = builder.Configuration["Jwt:Audience"] ?? "your_app_audience"; builder.Services.AddAuthentication(options => { // 默认认证方案设为Cookie,适配Web GUI客户端 options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme; }) .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options => { options.Cookie.Name = "SID"; options.Cookie.SameSite = SameSiteMode.Strict; }) .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwtIssuer, ValidAudience = jwtAudience, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)) }; // 适配SignalR:从查询参数读取token options.Events = new JwtBearerEvents { OnMessageReceived = context => { var token = context.Request.Query["access_token"]; if (!string.IsNullOrEmpty(token)) { context.Token = token; } return Task.CompletedTask; } }; }); // 配置Hub同时支持Cookie和JWT两种认证方案 app.MapHub<SocketHub>("/socket", options => { options.AuthenticationSchemes = new[] { CookieAuthenticationDefaults.AuthenticationScheme, JwtBearerDefaults.AuthenticationScheme }; }); app.UseAuthentication(); app.UseAuthorization();
2. 实现Hub的认证与用户组逻辑
完善SocketHub代码,处理控制台客户端的用户名密码验证、JWT签发,同时自动将认证用户加入对应用户组:
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.SignalR; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; public class SocketHub : Hub { private readonly IConfiguration _configuration; // 注入用户验证服务(替换为你的业务逻辑实现) private readonly IUserValidationService _userValidationService; public SocketHub(IConfiguration configuration, IUserValidationService userValidationService) { _configuration = configuration; _userValidationService = userValidationService; } public override async Task OnConnectedAsync() { // 检查用户是否已通过认证(Web端Cookie/控制台端JWT) if (Context.User.Identity?.IsAuthenticated == true) { var userName = Context.User.Identity.Name; // 将当前连接加入以用户名命名的组 await Groups.AddToGroupAsync(Context.ConnectionId, $"UserGroup_{userName}"); Console.WriteLine($"用户 {userName} 连接成功,加入组 UserGroup_{userName}"); } else { Console.WriteLine($"未认证客户端连接,ID: {Context.ConnectionId}"); } await base.OnConnectedAsync(); } // 控制台客户端调用此方法获取JWT public async Task<string> Authentication(string username, string password) { // 验证用户名密码(替换为数据库查询逻辑) var isValidUser = await _userValidationService.ValidateUser(username, password); if (!isValidUser) { throw new HubException("用户名或密码错误"); } // 创建用户Claims var claims = new List<Claim> { new Claim(ClaimTypes.Name, username), // 可添加角色等其他Claim new Claim(ClaimTypes.Role, "Admin") }; // 生成JWT Token var jwtKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:Key"])); var credentials = new SigningCredentials(jwtKey, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _configuration["Jwt:Issuer"], audience: _configuration["Jwt:Audience"], claims: claims, expires: DateTime.Now.AddHours(2), signingCredentials: credentials ); var tokenString = new JwtSecurityTokenHandler().WriteToken(token); // 验证通过后直接将当前连接加入用户组 await Groups.AddToGroupAsync(Context.ConnectionId, $"UserGroup_{username}"); return tokenString; } } // 示例用户验证服务(实际项目替换为你的业务实现) public interface IUserValidationService { Task<bool> ValidateUser(string username, string password); } public class UserValidationService : IUserValidationService { public async Task<bool> ValidateUser(string username, string password) { // 这里替换为数据库验证逻辑 return username == "Admin" && password == "YourPassword123"; } }
核心逻辑说明
- 多认证兼容:通过
MapHub指定AuthenticationSchemes,让Hub同时接受Cookie和JWT两种认证方式。 - JWT适配SignalR:控制台客户端首次无token连接,调用
Authentication方法验证后获取token,后续连接可通过access_token查询参数或请求头携带token,服务器自动读取验证。 - 用户组统一:无论Web端还是控制台端的认证用户,都会在连接时自动加入对应用户名的组,实现同一用户多客户端同组。
控制台客户端连接示例
// 首次无token连接 var connection = new HubConnectionBuilder() .WithUrl("http://localhost:5000/socket") .Build(); await connection.StartAsync(); // 调用认证方法获取token var token = await connection.InvokeAsync<string>("Authentication", "Admin", "YourPassword123"); // 使用token重新建立认证连接 var authenticatedConnection = new HubConnectionBuilder() .WithUrl("http://localhost:5000/socket", options => { options.AccessTokenProvider = () => Task.FromResult(token); }) .Build(); await authenticatedConnection.StartAsync();
内容的提问来源于stack exchange,提问作者Bandarbola855
相关产品推荐
相关产品推荐

