Identity Server 4返回自定义用户Claim
Identity Server 4返回自定义用户Claim
Identity Server系列目录
- Blazor Server访问Identity Server 4单点登录 - SunnyTrudeau - 博客园 (cnblogs.com)
- Blazor Server访问Identity Server 4单点登录2-集成Asp.Net角色 - SunnyTrudeau - 博客园 (cnblogs.com)
- Blazor Server访问Identity Server 4-手机验证码登录 - SunnyTrudeau - 博客园 (cnblogs.com)
- Blazor MAUI客户端访问Identity Server登录 - SunnyTrudeau - 博客园 (cnblogs.com)
- 在Identity Server 4项目集成Blazor组件 - SunnyTrudeau - 博客园 (cnblogs.com)
- Identity Server 4退出登录自动跳转返回 - SunnyTrudeau - 博客园 (cnblogs.com)
- Identity Server通过ProfileService返回用户角色 - SunnyTrudeau - 博客园 (cnblogs.com)
上一篇提到,尝试通过ProfileService返回更多用户声明,有的可以,有的不行。这个问题其实可以通过认证服务器的ProfileService 和客户端项目AddOpenIdConnect的options.ClaimActions.MapJsonKey配置解决。官网有提到ClaimActions.MapJsonKey:
在 ASP.NET Core 中保存外部提供程序的其他声明和令牌 | Microsoft Docs
options.ClaimActions.MapJsonKey("urn:google:picture", "picture", "url");
options.ClaimActions.MapJsonKey("urn:google:locale", "locale", "string");
修改Identity Server 4认证服务器项目
打开AspNetId4Web认证服务器项目,先升级用户实体类,增加一个字段:民族。
public class ApplicationUser : IdentityUser
{
/// <summary>
/// 民族
/// </summary>
public string Nation { get; set; }
}
生成一个数据库的迁移,打开程序包管理器控制台:
PM> Add-Migration nation
PM> Update-Database
在种子数据初始化时给alice和bob增加民族。
alice.Nation = "汉族";
bob.Nation = "壮族";
在ProfileService中,把民族添加到用户claims,注意,要从数据库中查询用户,获取Nation字段,context.Subject里边是没有的。
public class ProfileService : IProfileService { private readonly IServiceProvider _serviceProvider; public ProfileService(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public async Task GetProfileDataAsync(ProfileDataRequestContext context) { using var scope = _serviceProvider.CreateScope(); var userMgr = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>(); //按Name找不到 //var user = await userMgr.FindByNameAsync(context.Subject.Identity.Name); //按Sub找得到 string userId = context.Subject.FindFirstValue(JwtClaimTypes.Subject); var user = await userMgr.FindByIdAsync(userId); var nameClaim = context.Subject.FindAll(JwtClaimTypes.Name); context.IssuedClaims.AddRange(nameClaim); var roleClaims = context.Subject.FindAll(JwtClaimTypes.Role); context.IssuedClaims.AddRange(roleClaims); var emailClaims = context.Subject.FindAll(JwtClaimTypes.Email); context.IssuedClaims.AddRange(emailClaims); var phoneNumberClaims = context.Subject.FindAll(JwtClaimTypes.PhoneNumber); context.IssuedClaims.AddRange(phoneNumberClaims); //获取民族字段 var nationClaim = new Claim("nation", user.Nation); context.IssuedClaims.Add(nationClaim); await Task.CompletedTask; } public async Task IsActiveAsync(IsActiveContext context) { await Task.CompletedTask; }
修改Blazor Server客户端项目
在BlzOidc项目中,配置oidc参数时,添加映射的字段nation,上一篇测试发现PhoneNumber字段无法返回,添加映射也可以解决。
//添加认证相关的服务 private static void ConfigureAuthServices(IServiceCollection services) { //清除微软定义的clamis JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); //默认采用cookie认证方案,添加oidc认证方案 services.AddAuthentication(options => { options.DefaultScheme = "cookies"; options.DefaultChallengeScheme = "oidc"; }) //配置cookie认证 .AddCookie("cookies") .AddOpenIdConnect("oidc", options => { //id4服务的地址 options.Authority = "https://localhost:5001"; //id4配置的ClientId以及ClientSecrets options.ClientId = "BlazorServerOidc"; options.ClientSecret = "BlazorServerOidc.Secret"; //认证模式 options.ResponseType = "code"; //保存token到本地 options.SaveTokens = true; //很重要,指定从Identity Server的UserInfo地址来取Claim //效果等同id4配置AlwaysIncludeUserClaimsInIdToken = true options.GetClaimsFromUserInfoEndpoint = true; //指定要取哪些资料(除Profile之外,Profile是默认包含的) options.Scope.Add("scope1"); //通过ProfileService返回用户角色 //options.Scope.Add("role"); //映射自定义用户声明 options.ClaimActions.MapJsonKey(JwtClaimTypes.PhoneNumber, JwtClaimTypes.PhoneNumber); options.ClaimActions.MapJsonKey("nation", "nation"); //这里是个ClaimType的转换,Identity Server的ClaimType和Blazor中间件使用的名称有区别,需要统一。 //User.Identity.Name=JwtClaimTypes.Name options.TokenValidationParameters.NameClaimType = "name"; options.TokenValidationParameters.RoleClaimType = "role"; options.Events.OnUserInformationReceived = (context) => { //id4返回的角色是字符串数组或者字符串,blazor server的角色是字符串,需要转换,不然无法获取到角色 ClaimsIdentity claimsId = context.Principal.Identity as ClaimsIdentity; var roleElement = context.User.RootElement.GetProperty(JwtClaimTypes.Role); if (roleElement.ValueKind == System.Text.Json.JsonValueKind.Array) { var roles = roleElement.EnumerateArray().Select(e => e.ToString()); claimsId.AddClaims(roles.Select(r => new Claim(JwtClaimTypes.Role, r))); } else { claimsId.AddClaim(new Claim(JwtClaimTypes.Role, roleElement.ToString())); } return Task.CompletedTask; }; }); }
VS2022调试运行AspNetId4Web认证服务器项目和BlzOidc项目,在BlzOidc主页点击登录,跳转到AspNetId4Web认证服务器,输入手机验证码登录,跳转回到BlzOidc主页,可以看到,想要的用户信息都有了
DEMO代码地址:https://gitee.com/woodsun/blzid4