这似乎不是 IdentityServer4 提供的功能(欢迎任何矛盾的 cmets)。所以我最终使用声明将文化信息传回给我的客户。
所以我创建了一个继承自IProfileService 的类,因此我可以将附加声明JwtClaimTypes.Locale 加载到idToken。然而,当它运行时,它似乎与运行它的用户处于不同的上下文中,因此CultureInfo.CurrentCulture 设置为与我期望的不同的语言环境(例如,UI 设置为pl-PL 但在配置文件中服务,它被设置为en-US)。所以我最终创建了一个InMemoryUserInfo 类,它基本上是一个包装好的ConcurrentDictionary,其中包含我的用户ID 和一个包含用户所选语言环境的对象。每当用户更改首选语言或从数据库提供用户语言时,我都会创建条目/更新该词典。无论如何,InMemoryUserInfo 然后被注入到我的配置文件服务中,在那里它被添加为另一个声明:
public class IdentityWithAdditionalClaimsProfileService : IProfileService
{
private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
private readonly UserManager<ApplicationUser> _userManager;
/// <summary>
/// This services is running in a different thread then UI, so
/// when trying to obtain CultureInfo.CurrentUICulture, it not necessarily
/// is going to be correct. So whenever culture is changed,
/// it is stored in InMemoryUserInfo. Current user's culture will
/// be included in a claim.
/// </summary>
private readonly InMemoryUserInfo _userInfo;
public IdentityWithAdditionalClaimsProfileService(
IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory,
UserManager<ApplicationUser> userManager,
InMemoryUserInfo userInfo)
{
_claimsFactory = claimsFactory;
_userManager = userManager;
_userInfo = userInfo;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
var principal = await _claimsFactory.CreateAsync(user);
var claims = principal.Claims.ToList();
claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();
claims.Add(new Claim(JwtClaimTypes.Locale, _userInfo.Get(user.Id).Culture ?? throw new ArgumentNullException()));
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
context.IsActive = user != null;
}
}
记得在DI注册IProfileService
services.AddTransient<IProfileService, IdentityWithAdditionalClaimsProfileService>();
之后,在我的客户启动中,我分析了OpenIdConnectEvents 中的声明,并将 cookie 设置为从 IdentityServer 接收的文化:
.AddOpenIdConnect("oidc", options =>
{
options.Events = new OpenIdConnectEvents
{
OnTicketReceived = context =>
{
//Goes through returned claims from authentication endpoint and looks for
//localization info. If found and different, then new CultureInfo is set.
string? culture = context.Principal?.FindFirstValue(JwtClaimTypes.Locale);
if (culture != null && CultureInfo.CurrentUICulture.Name != culture)
{
context.HttpContext.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(
new RequestCulture(culture, culture)),
new CookieOptions
{ Expires = DateTimeOffset.UtcNow.AddYears(1) }
);
}
return Task.CompletedTask;
};
}
});