Azure AD B2C本地账户注册策略:将displayName默认值设为givenName
我之前在配置本地账户注册的时候也碰到过类似的情况——静态的DefaultValue确实没法动态引用其他Claim的值,咱们换个思路来解决:
为什么原来的配置不生效?
原来的<PersistedClaim ClaimTypeReferenceId="displayName" DefaultValue="unknown" />里的DefaultValue是静态字符串,它只能设置固定值,没办法自动读取givenName的内容来填充,所以得用动态逻辑来处理这个默认值。
三种可行的解决方案
1. 注册时直接手动赋值(最直接)
如果是你自己实现的注册接口,在创建用户实体的时候,直接判断:如果用户没填displayName,就把givenName的值赋给它。示例代码:
// 假设这是你的注册模型和用户实体 var registerModel = /* 前端传过来的注册数据 */; var user = new ApplicationUser { UserName = registerModel.Email, Email = registerModel.Email, GivenName = registerModel.GivenName, // 核心逻辑:displayName为空时用givenName填充 DisplayName = string.IsNullOrWhiteSpace(registerModel.DisplayName) ? registerModel.GivenName : registerModel.DisplayName }; // 然后保存用户到数据库 await _userManager.CreateAsync(user, registerModel.Password);
这样用户数据在持久化的时候,displayName就已经是givenName的默认值了,不需要依赖Claim配置的静态默认值。
2. 用Profile Service动态生成Claim(适合多场景复用)
如果你的系统用了Identity Server或者ASP.NET Core Identity的Profile Service,可以在生成用户Claim的时候动态处理:当displayName不存在时,自动用givenName填充。
首先修改你的Profile Service实现:
public class CustomProfileService : IProfileService { private readonly UserManager<ApplicationUser> _userManager; public CustomProfileService(UserManager<ApplicationUser> userManager) { _userManager = userManager; } public async Task GetProfileDataAsync(ProfileDataRequestContext context) { var user = await _userManager.GetUserAsync(context.Subject); var issuedClaims = new List<Claim>(context.Subject.Claims); // 获取givenName和已有的displayName var givenName = issuedClaims.FirstOrDefault(c => c.Type == ClaimTypes.GivenName)?.Value; var displayName = issuedClaims.FirstOrDefault(c => c.Type == ClaimTypes.Name)?.Value; // displayName通常对应Name类型 // 如果displayName为空且givenName存在,添加这个Claim if (string.IsNullOrWhiteSpace(displayName) && !string.IsNullOrWhiteSpace(givenName)) { issuedClaims.Add(new Claim(ClaimTypes.Name, givenName)); } context.IssuedClaims = issuedClaims; } // 其他必要的方法实现(比如IsActiveAsync) public async Task IsActiveAsync(IsActiveContext context) { var user = await _userManager.GetUserAsync(context.Subject); context.IsActive = user != null; } }
然后把原来的PersistedClaim配置改成不带DefaultValue的版本:
<PersistedClaim ClaimTypeReferenceId="displayName" />
这样每次生成用户Claim的时候,都会自动检查并填充displayName的默认值。
3. 自定义UserStore持久化时处理(适合数据库层面的默认值)
如果你希望数据库里的displayName字段默认就是givenName,可以自定义UserStore,在创建用户的时候自动赋值:
public class CustomUserStore : UserStore<ApplicationUser> { public CustomUserStore(DbContext context, IdentityErrorDescriber describer = null) : base(context, describer) { } public override async Task CreateAsync(ApplicationUser user, CancellationToken cancellationToken) { // 持久化前检查并赋值 if (string.IsNullOrWhiteSpace(user.DisplayName) && !string.IsNullOrWhiteSpace(user.GivenName)) { user.DisplayName = user.GivenName; } await base.CreateAsync(user, cancellationToken); } }
然后在Startup里替换默认的UserStore:
services.AddIdentity<ApplicationUser, IdentityRole>() .AddUserStore<CustomUserStore>() .AddDefaultTokenProviders();
总结
静态的DefaultValue没办法实现动态引用其他Claim的需求,所以推荐用第一种注册时手动赋值的方式,简单直接;如果需要在多个场景(比如登录、获取用户信息接口)都生效,就用Profile Service的方案;如果要确保数据库里的字段默认值正确,就用自定义UserStore的方式。
内容的提问来源于stack exchange,提问作者Lucky

