IdentityServer4+AspNet Core 2.0:修改用户文化偏好后静默重登更新Claims
嘿,结合你用的IdentityServer4、ASP.NET Core 2.0和ASP.NET Identity技术栈,我给你梳理下完整的实现方案,刚好之前做过类似需求,亲测可行:
1. 把用户偏好Culture注入到Claims中
首先得确保用户的偏好Culture能被添加到Claims集合里,最优雅的方式是自定义UserClaimsPrincipalFactory,在生成用户Claims的时候自动注入:
public class CustomUserClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser> { public CustomUserClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, IOptions<IdentityOptions> optionsAccessor) : base(userManager, optionsAccessor) { } protected override async Task<ClaimsIdentity> GenerateClaimsAsync(ApplicationUser user) { var identity = await base.GenerateClaimsAsync(user); // 从用户实体中取出数据库存储的偏好Culture,添加到Claims if (!string.IsNullOrEmpty(user.PreferredCulture)) { identity.AddClaim(new Claim("culture", user.PreferredCulture)); } return identity; } }
然后在Startup.cs里注册这个自定义Factory,替换默认实现:
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, CustomUserClaimsPrincipalFactory>();
2. 自定义RequestCultureProvider读取Claims中的Culture
接下来实现专属的RequestCultureProvider,优先从当前登录用户的Claims里读取Culture值,这样就能覆盖默认的本地化逻辑:
public class ClaimsRequestCultureProvider : RequestCultureProvider { public override Task<ProviderCultureResult> DetermineProviderCultureResult(HttpContext httpContext) { if (httpContext.User.Identity.IsAuthenticated) { var cultureClaim = httpContext.User.FindFirst("culture"); if (cultureClaim != null && !string.IsNullOrEmpty(cultureClaim.Value)) { return Task.FromResult(new ProviderCultureResult(cultureClaim.Value)); } } // 如果没找到Claims里的Culture,就交给后续的Provider处理(比如Cookie、QueryString) return Task.FromResult<ProviderCultureResult>(null); } }
然后在Startup的本地化配置里,把这个Provider放到最前面,确保优先级最高:
services.Configure<RequestLocalizationOptions>(options => { var supportedCultures = new[] { new CultureInfo("en-US"), new CultureInfo("zh-CN"), // 这里添加你支持的其他文化 }; options.DefaultRequestCulture = new RequestCulture("en-US"); options.SupportedCultures = supportedCultures; options.SupportedUICultures = supportedCultures; // 把自定义Provider插入到最前面 options.RequestCultureProviders.Insert(0, new ClaimsRequestCultureProvider()); });
3. 修改个人资料后静默更新Claims
当用户在个人资料页修改并保存偏好Culture到数据库后,不需要让用户手动重新登录,用SignInManager的RefreshSignInAsync方法就能静默更新Claims:
在你的个人资料Controller的保存方法里加入这段逻辑:
[HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> UpdateProfile(ProfileViewModel model) { if (ModelState.IsValid) { var user = await _userManager.GetUserAsync(User); if (user == null) { return NotFound(); } // 更新数据库里的用户偏好Culture user.PreferredCulture = model.PreferredCulture; var result = await _userManager.UpdateAsync(user); if (result.Succeeded) { // 关键:静默重新生成Claims并更新登录Cookie,用户完全无感知 await _signInManager.RefreshSignInAsync(user); // 重定向回个人资料页,此时新的Culture已经生效 return RedirectToAction(nameof(Profile)); } foreach (var error in result.Errors) { ModelState.AddModelError(string.Empty, error.Description); } } return View(model); }
RefreshSignInAsync会重新调用你之前自定义的UserClaimsPrincipalFactory生成新的ClaimsPrincipal,自动包含更新后的Culture值,然后替换掉当前的登录Cookie,整个过程用户完全感觉不到,但Claims已经更新完成了。
4. 验证效果
你可以在视图里用@CultureInfo.CurrentCulture.Name查看当前生效的Culture,或者在接口里打印HttpContext.RequestCulture.Culture.Name,确认修改后的值是否正确生效。
内容的提问来源于stack exchange,提问作者jdcorr

