ASP.NET Core SPA模板中如何在Identity Core令牌响应内添加角色
基于ASP.NET Core内置React授权模板开发时,AddApiAuthorization 扩展默认实现的IdentityServer声明生成逻辑,仅会将用户ID、用户名、邮箱等基础身份声明写入访问令牌载荷,不会主动拉取IdentityRole关联的角色信息填充到返回令牌中。即便已经通过AddRoles<IdentityRole>()完成了角色服务注册,也不会自动将角色作为令牌输出内容,需要手动实现自定义的声明生成逻辑才能把角色信息写入令牌。
实现自定义Profile服务,重写声明填充逻辑
新建自定义类继承IdentityServer内置的ProfileService,重写GetProfileDataAsync方法,在默认声明生成逻辑的基础上,追加读取当前用户关联角色、写入令牌声明的逻辑,示例代码如下:public class CustomProfileService : ProfileService { private readonly UserManager<ApplicationUser> _userManager; public CustomProfileService( UserManager<ApplicationUser> userManager, IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory, IOptions<IdentityOptions> identityOptions, ILogger<ProfileService> logger, IHttpContextAccessor httpContextAccessor) : base(claimsFactory, identityOptions, logger, httpContextAccessor) { _userManager = userManager; } public override async Task GetProfileDataAsync(ProfileDataRequestContext context) { // 执行默认逻辑,填充用户基础身份声明 await base.GetProfileDataAsync(context); var currentUser = await _userManager.GetUserAsync(context.Subject); if (currentUser == null) return; // 查询当前用户的所有关联角色 var userRoleList = await _userManager.GetRolesAsync(currentUser); // 将角色信息作为声明追加到令牌输出集合 foreach (var roleName in userRoleList) { context.IssuedClaims.Add(new Claim(ClaimTypes.Role, roleName)); } } }注意:需要在默认声明逻辑的基础上追加内容,不要直接覆盖默认声明集合,否则会丢失原有基础身份字段。
将自定义Profile服务注册到IdentityServer依赖容器
在原有IdentityServer服务配置的链式调用中,追加自定义Profile服务的注册逻辑,调整后的完整服务配置如下:services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true) .AddRoles<IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>(); services.AddIdentityServer() .AddApiAuthorization<ApplicationUser, ApplicationDbContext>() // 新增注册自定义声明服务 .AddProfileService<CustomProfileService>(); services.AddAuthentication() .AddIdentityServerJwt();补充JWT角色声明映射配置
即使令牌中已经包含角色声明,如果JWT校验环节没有正确映射角色声明类型,后端的角色授权逻辑会无法识别角色信息。需要在JWT配置中指定角色声明的匹配规则:services.AddAuthentication() .AddIdentityServerJwt() .AddJwtBearer(options => { // 指定授权逻辑识别的角色声明类型,和写入令牌的角色声明类型对齐 options.TokenValidationParameters.RoleClaimType = ClaimTypes.Role; });效果验证
调整完成后重启项目,重新走登录流程获取新的访问令牌,通过令牌解析工具查看载荷即可看到角色字段;本地存储中留存的新令牌也会包含对应的角色信息。
- 不要通过静态给用户Claims赋值的方式写入角色:这种方式无法同步用户角色的动态变更,只要后续在系统中调整了用户角色,令牌中的内容就会和数据库实际状态不一致。
- 重写ProfileService方法时不能跳过
base.GetProfileDataAsync的调用:跳过该逻辑会丢失令牌中原有的用户ID、用户名等基础声明,直接导致身份校验异常。 - 配置调整完成后需要重新登录获取新令牌:旧令牌是调整前签发的,不包含新增的角色声明,只有重新走授权流程拿到的新令牌才会携带角色信息。
内容的提问来源于stack exchange,提问作者hik hyper

