返回"ui_locale"回到客户 [英] Return "ui_locale" back to client
问题描述
因此,我知道如何使IdentityServer4应用程序使用具有挑战性的客户所拥有的文化.通过定义
So I know how to make IdentityServer4 app to use culture that the challenging client has. By defining
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.UiLocales = "pl-PL";
return Task.CompletedTask;
},
}
我可以使IdentityServer4也向我显示"pl-PL"中的登录页面.但是,诀窍在于,我允许用户在登录屏幕上更改语言.如何在登录时通知客户文化信息已更改?当前,我的客户端甚至不显示任何页面,直接进入登录屏幕(因此,从客户端应用程序浏览器直接重定向到IdentityServer4应用程序,用户可以在其中更改其语言).
I can make IdentityServer4 to also show me login page in "pl-PL". The trick is however, that I allow users to change the language on the login screen. How can I inform the client that culture info was changed during login? Currently my client does not even show any page, goes directly to Login screen (thus from client app browser is redirected immediately to IdentityServer4 app, where a user can change his/her language).
推荐答案
似乎这不是IdentityServer4提供的功能(欢迎任何矛盾的评论).因此,我最终使用索赔将文化信息传回给我的客户.因此,我创建了一个继承自 IProfileService
的类,以便可以将其他声明 JwtClaimTypes.Locale
加载到idToken中.但是似乎它在运行时处于与运行它的用户不同的上下文中,因此将 CultureInfo.CurrentCulture
设置为与我期望的语言环境不同的语言环境(例如,UI设置 pl-PL
,但在配置文件服务中将其设置为 en-US
).因此,我最终创建了一个 InMemoryUserInfo
类,该类基本上是一个包装的 ConcurrentDictionary
,其中包含我的用户ID和一个包含用户所选区域设置的对象.每当用户更改首选语言或从数据库传递用户语言时,我都会创建该字典的条目/更新.无论如何,然后将 InMemoryUserInfo
注入到我的配置文件服务中,并在其中添加它作为另一个声明:
It seems that this is not a functionality that IdentityServer4 offers (any contradictory comments welcome). So I ended up with using claims to pass the culture information back to my client.
So I created a class inheriting from IProfileService
so I can load additional claim JwtClaimTypes.Locale
to the idToken. However it seems that when it is running, it is in a different context then the user it runs for, so CultureInfo.CurrentCulture
is set to a different locale than what I was expecting (for example the UI was set pl-PL
but inside profile service, it was set to en-US
). So I ended up with creating a InMemoryUserInfo
class that is basically a wrapped ConcurrentDictionary
that contains my user id and an object that contains user's selected locale. I create entry/update that dictionary, whenever user changes the preferred language or when a user language is delivered from the database. Anyway, that InMemoryUserInfo
is then injected into my profile service where it is added as another claim:
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接收到的区域性:
Afterwards, in my client's startup, I analyse the claims in OpenIdConnectEvents
and set the cookie to culture received from 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;
};
}
});
这篇关于返回"ui_locale"回到客户的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!