这是一个复杂的东西,我查找了一些混乱的资料,最终在混乱的状态成功了,然后我尝试阅读代码,并删除了一些重复的东西,并以一种较为简洁的方式成功。
示例hubs端点
app.MapHub<MessageHub>("/hubs/message");
我假设你已经知道如何添加SignalR和通常的使用jwt的验证。
这包括
处理跨域, 添加身份,数据库,账户,驱动,身份验证。
在这种情况下,只需要额外添加
builder.Services.AddAuthentication(options =>{options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(options =>{options.TokenValidationParameters = new TokenValidationParameters{ValidateIssuer = true,ValidateAudience = true,ValidateLifetime = true,ValidateIssuerSigningKey = true,ValidIssuer = jswSettings.Issuer,ValidAudience = jswSettings.Audience,IssuerSigningKey = new SymmetricSecurityKey(secretKey)};options.Events = new JwtBearerEvents();});
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IPostConfigureOptions<JwtBearerOptions>,ConfigureJwtBearerOptions>());
其中
ConfigureJwtBearerOptions 是自定义的类,这个设计思路来自于微软的指南,是通过添加类似于中间件的形式实现。
如果不添加
options.Events = new JwtBearerEvents();
会在后面的(上面的)组件中提示空对象。
using Microsoft.IdentityModel.Tokens;using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Options;
public class ConfigureJwtBearerOptions : IPostConfigureOptions<JwtBearerOptions>
{public void PostConfigure(string? name, JwtBearerOptions options){var originalOnMessageReceived = options.Events.OnMessageReceived;options.Events.OnMessageReceived = async context =>{await originalOnMessageReceived(context);if (string.IsNullOrEmpty(context.Token)){var accessToken = context.Request.Query["access_token"];var path = context.HttpContext.Request.Path;if (!string.IsNullOrEmpty(accessToken) &&path.StartsWithSegments("/hubs/message")){context.Token = accessToken;}}};options.Events.OnAuthenticationFailed = context =>{if (context.Exception.GetType() == typeof(SecurityTokenExpiredException)){context.Response.Headers.Append("Token-Expired", "true");}else if (context.Exception.GetType() == typeof(SecurityTokenInvalidLifetimeException)){context.Response.Headers.Append("Token-Expired", "true");}return Task.CompletedTask;};}
}
这个可以忽略
options.Events.OnAuthenticationFailed
主要的逻辑是 设置上下文的 token。
最后在中心上设置相应的要求
[Authorize(Policy = "user")] public class MessageHub: Hub
这里使用的是之前配置的身份验证。
builder.Services.AddAuthorization(options => {options.AddPolicy("admin", policy => policy.RequireRole(["admin"]));options.AddPolicy("user", policy => policy.RequireRole(["user"]));options.AddPolicy("vip", policy => policy.RequireRole(["vip"])); });
token中包含身份信息。在分发的token中包含。
private string GenerateToken(string username, int id){var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"]!));var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);var roles = _context.AccountRoles!.Where(a => a.AccountId == id).Include(r=>r.Role).ToList();var claims = new List<Claim> { new Claim(ClaimTypes.Name, username) };roles.ForEach(role =>{claims.Add(new Claim(ClaimTypes.Role,role.Role!.Name!));});var token = new JwtSecurityToken(issuer: _configuration["Jwt:Issuer"],audience: _configuration["Jwt:Audience"],claims: claims,expires: DateTime.Now.AddDays(3),signingCredentials: credentials);return new JwtSecurityTokenHandler().WriteToken(token);}