ASP.NET Core 5.0 JWT认证下UserId与UserName声明值颠倒问题
问题原因
这不是ASP.NET Core 5.0 Identity组件的原生bug,是配置不完整+JWT生成逻辑错位导致的:
UserManager.GetUserAsync内部会从传入的ClaimsPrincipal中读取Options.ClaimsIdentity.UserIdClaimType对应的声明值,尝试转换为用户主键类型(int)后查询用户。你当前拿到的声明中用户ID和用户名完全颠倒,本质是生成JWT令牌时给两个声明赋错了值。- 你使用
AddIdentityCore注册Identity服务时,没有显式指定声明类型映射规则,且JwtBearer默认开启的入站声明映射可能改变声明类型匹配关系,进一步加剧了声明错位的问题。 - 现有Identity配置不完整,仅调用
AddEntityFrameworkStores缺少必要的服务注册,后续调用其他Identity接口也可能出现异常。
修复方案
按以下步骤调整即可解决:
修正JWT生成逻辑,确保声明赋值正确
生成JWT时,必须将用户主键值赋值给UserIdClaimType对应的声明,用户名赋值给UserNameClaimType对应的声明,不要写反。参考正确实现:// 假设已经通过用户名密码校验拿到了user对象 var claims = new List<Claim> { // 用户主键赋值给NameIdentifier(Identity默认的UserId声明类型) new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), // 用户名赋值给Name(Identity默认的UserName声明类型) new Claim(ClaimTypes.Name, user.UserName), // 其余角色、自定义业务声明按需添加 }; var signingKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(你的JWT密钥)); var credentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256); var securityToken = new JwtSecurityToken( issuer: 你的JWT签发方, audience: 你的JWT受众, claims: claims, expires: DateTime.Now.AddHours(过期时长), signingCredentials: credentials ); var token = new JwtSecurityTokenHandler().WriteToken(securityToken);如果你习惯使用JWT标准短声明(
sub存用户ID、name存用户名),后续Identity配置要对应调整声明类型,保持两边完全一致即可。补全Identity配置,显式指定声明映射规则
修改ConfigureServices中的Identity注册代码,显式声明使用的声明类型,补全必要服务:services.AddIdentityCore<IdentityUser<int>>(options => { // 显式指定用户ID、用户名对应的声明类型,和JWT生成逻辑保持一致 // 如果用JWT标准短声明,这里就写 options.ClaimsIdentity.UserIdClaimType = "sub"; options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier; options.ClaimsIdentity.UserNameClaimType = ClaimTypes.Name; // 密码规则、登录校验等其余配置按需设置 }) .AddEntityFrameworkStores<MyUserContext>() .AddDefaultTokenProviders(); // 补全默认令牌服务,避免后续账号相关操作异常关闭JwtBearer的默认入站声明映射
JwtBearer认证默认会将JWT原生短声明映射为WS-*规范的长声明类型,容易出现匹配错位,在JwtBearer配置中关闭该特性:services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { // 关闭默认声明映射,保持JWT内声明和写入时完全一致 options.MapInboundClaims = false; options.TokenValidationParameters = new TokenValidationParameters { // 保留你原有的令牌验证配置:ValidateIssuer、ValidateAudience、IssuerSigningKey等 }; }) .AddApplicationCookie();校验修复结果
部署后在受保护接口断点查看Context.User.Claims,确认UserIdClaimType对应的值为int格式的用户ID、UserNameClaimType对应的值为用户名,此时调用userManager.GetUserAsync(Context.User)即可正常返回用户对象,不会出现int类型转换异常。
内容的提问来源于stack exchange,提问作者Christoph Mett
相关产品推荐
相关产品推荐

