ASP.NET Core中如何为匿名用户实现带声明令牌的身份识别?
ASP.NET Core 中半匿名用户的声明式身份识别方案
你完全可以利用ASP.NET Core的基于声明的认证系统(ASP.NET Core Identity底层依赖这套系统)来实现半匿名用户的持久化识别,不需要依赖显式登录流程,以下是成熟的落地方案:
核心思路
不启用ASP.NET Core Identity的用户注册/登录模块,直接使用Cookie认证中间件(或自定义Header传递)为匿名用户颁发包含NameIdentifier声明的身份凭证,让服务器能通过Context.User.Claims识别用户,同时通过Cookie或本地存储跨会话保留用户ID。
方案一:Cookie存储匿名ID(推荐,安全性更高)
1. 配置Cookie认证中间件
在Program.cs中注册Cookie认证服务,自定义认证Scheme和Cookie属性:
var builder = WebApplication.CreateBuilder(args); // 注册Cookie认证,自定义Scheme名称 builder.Services.AddAuthentication("AnonymousAuth") .AddCookie("AnonymousAuth", options => { options.Cookie.Name = "AnonymousUser"; options.ExpireTimeSpan = TimeSpan.FromDays(365); // 长期保存用户ID options.SlidingExpiration = true; // 活动时自动延长有效期 options.Cookie.HttpOnly = true; // 防止XSS窃取 options.Cookie.SameSite = SameSiteMode.Lax; options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // 仅HTTPS环境下发送 }); // 添加SignalR服务 builder.Services.AddSignalR(); var app = builder.Build(); // 中间件顺序:路由 -> 认证 -> 自定义匿名用户处理 -> 授权 -> 端点 app.UseRouting(); app.UseAuthentication(); // 自定义中间件:生成/验证匿名用户ID app.Use(async (context, next) => { // 仅处理未认证的请求 if (!context.User.Identity.IsAuthenticated) { // 从Cookie读取已存在的匿名ID var anonymousId = context.Request.Cookies["AnonymousUser"]; // 不存在则生成新的GUID if (string.IsNullOrEmpty(anonymousId)) { anonymousId = Guid.NewGuid().ToString(); } // 创建包含NameIdentifier声明的身份标识 var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, anonymousId) }; var identity = new ClaimsIdentity(claims, "AnonymousAuth"); var principal = new ClaimsPrincipal(identity); // 登录用户,将身份写入Cookie await context.SignInAsync("AnonymousAuth", principal, new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.AddYears(1) }); } await next(); }); app.UseAuthorization(); // 映射SignalR Hub app.MapHub<GameHub>("/gameHub"); app.Run();
2. SignalR中获取用户ID
在Hub类中直接通过Context.User读取声明:
public class GameHub : Hub { public async Task SendGameAction(string action) { // 获取匿名用户ID var userId = Context.User.FindFirst(ClaimTypes.NameIdentifier)?.Value; // 执行业务逻辑,比如广播动作或保存游戏状态 await Clients.Others.SendAsync("ReceiveGameAction", userId, action); } }
方案二:本地存储(localStorage)+ Header传递
如果不想使用Cookie,可以让前端生成并存储匿名ID,通过请求Header传递给后端:
1. 前端代码(生成/存储ID)
// 初始化匿名用户ID let anonymousId = localStorage.getItem('AnonymousUserId'); if (!anonymousId) { anonymousId = crypto.randomUUID(); localStorage.setItem('AnonymousUserId', anonymousId); } // SignalR连接时添加Header const connection = new signalR.HubConnectionBuilder() .withUrl("/gameHub", { headers: { "Anonymous-Id": anonymousId } }) .build(); // 启动连接 connection.start().catch(err => console.error(err));
2. 后端中间件(读取Header并生成身份)
在Program.cs中添加自定义中间件,替代Cookie认证的登录逻辑:
app.Use(async (context, next) => { if (!context.User.Identity.IsAuthenticated) { // 从Header读取匿名ID var anonymousId = context.Request.Headers["Anonymous-Id"].FirstOrDefault(); // 不存在则生成新ID并返回给前端 if (string.IsNullOrEmpty(anonymousId)) { anonymousId = Guid.NewGuid().ToString(); context.Response.Headers.Add("Anonymous-Id", anonymousId); } // 创建身份标识 var claims = new List<Claim> { new Claim(ClaimTypes.NameIdentifier, anonymousId) }; var identity = new ClaimsIdentity(claims, "AnonymousAuth"); context.User = new ClaimsPrincipal(identity); } await next(); });
与ASP.NET Core Identity的兼容
如果后续需要支持用户注册升级(匿名用户转为正式注册用户),可以在Identity的ApplicationUser类中添加AnonymousId字段:
public class ApplicationUser : IdentityUser { public string? AnonymousId { get; set; } }
用户注册时,将当前的匿名ID关联到新用户,即可保留之前的游戏数据。
注意事项
- 用户清除Cookie或localStorage会生成新ID,这是预期行为,无需额外处理。
- 若使用Cookie方案,确保网站启用HTTPS,避免Cookie被窃取。
- 不要在匿名用户的声明中包含敏感信息,仅用于身份识别。
内容的提问来源于stack exchange,提问作者Jez
相关产品推荐
相关产品推荐

