ASP.NET Core 2.0 SignalR:如何从授权需求向Hub传递用户详情
嘿,这个问题我刚好碰到过,给你几个优雅的解决方案,不用纠结ASP.NET Identity也能轻松搞定!
首先,先修正你Startup里的一个小问题:你之前在添加授权策略时用services.BuildServiceProvider().GetRequiredService<AccessTokenRequirement>()的方式不太合适,会额外创建一个服务容器,容易引发生命周期问题。咱们先重构一下授权逻辑:
1. 拆分授权需求与处理逻辑
把你的AccessTokenRequirement拆成一个纯标记类(只实现IAuthorizationRequirement),然后单独写一个处理类来做验证和用户信息提取:
// 纯标记类,用来标识授权需求 public class AccessTokenRequirement : IAuthorizationRequirement { } // 授权处理类,注入数据库上下文来查询用户 public class AccessTokenRequirementHandler : AuthorizationHandler<AccessTokenRequirement> { private readonly YourDbContext _dbContext; public AccessTokenRequirementHandler(YourDbContext dbContext) { _dbContext = dbContext; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, AccessTokenRequirement requirement) { // 获取当前HTTP上下文 if (context.Resource is not HttpContext httpContext) { context.Fail(); return; } // 从请求头拿AccessToken var accessToken = httpContext.Request.Headers["AccessToken"].FirstOrDefault(); if (string.IsNullOrWhiteSpace(accessToken)) { context.Fail(); return; } // 验证Token并查询用户 var user = await _dbContext.Users.FirstOrDefaultAsync(u => u.AccessToken == accessToken); if (user == null) { context.Fail(); return; } // 重点:把用户信息转为Claims,添加到用户身份中 var userClaims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // 用户ID new Claim(ClaimTypes.Name, user.UserName), // 用户名 new Claim("Email", user.Email) // 自定义Claim,存邮箱等其他信息 }; var identity = new ClaimsIdentity(userClaims, "AccessTokenAuth"); context.User.AddIdentity(identity); // 标记授权通过 context.Succeed(requirement); } }
2. 修正Startup的授权配置
先注册授权处理类,再添加策略,这样DI会自动注入所需的服务:
// 注册授权处理类 services.AddScoped<IAuthorizationHandler, AccessTokenRequirementHandler>(); services.AddAuthorization(options => { options.AddPolicy("AccessToken", policy => { // 直接new需求类就行,处理类会被DI自动找到 policy.Requirements.Add(new AccessTokenRequirement()); }); });
3. 在Hub里轻松获取用户信息
现在你在Hub方法里,直接通过Context.User就能拿到用户的所有信息了!比如:
[Authorize(Policy = "AccessToken")] public class MyHub : Hub { public async Task SendMessage(string message) { // 从Claims里提取用户信息 var userId = Context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; var userName = Context.User.FindFirst(ClaimTypes.Name)?.Value; var userEmail = Context.User.FindFirst("Email")?.Value; // 用这些信息做业务逻辑,比如发送带用户名的消息 await Clients.All.SendAsync("ReceiveMessage", userName, message); } }
进阶:封装扩展方法简化调用
如果觉得每次写FindFirst太麻烦,可以封装一个扩展方法:
public static class ClaimsPrincipalExtensions { public static string GetUserId(this ClaimsPrincipal principal) { return principal.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? string.Empty; } public static string GetUserName(this ClaimsPrincipal principal) { return principal.FindFirst(ClaimTypes.Name)?.Value ?? string.Empty; } public static string GetUserEmail(this ClaimsPrincipal principal) { return principal.FindFirst("Email")?.Value ?? string.Empty; } }
然后Hub里就能更简洁地调用:
var userId = Context.User.GetUserId(); var userName = Context.User.GetUserName();
备选方案:用HttpContext.Items存储用户对象
如果你不想用Claims,也可以在授权处理类里把用户对象直接存到HttpContext.Items:
// 在HandleRequirementAsync里验证通过后添加 httpContext.Items["CurrentUser"] = user;
然后在Hub里通过Context.GetHttpContext()获取:
var httpContext = Context.GetHttpContext(); var currentUser = httpContext?.Items["CurrentUser"] as YourUserModel;
不过更推荐用Claims的方式,因为它符合ASP.NET的授权标准,SignalR的Context.User也是基于这个设计的,扩展性更好。
内容的提问来源于stack exchange,提问作者fischja
相关产品推荐
相关产品推荐

