返回"ui_locale"回到客户 [英] Return "ui_locale" back to client

查看:269
本文介绍了返回"ui_locale"回到客户的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我知道如何使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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆