@context.user.identity.name 在使用 .mapuniquejsonkey 时为 null

@context.user.identity.name is null when using .mapuniquejsonkey

我有两个应用程序,Blazor 和 IdentityServer。我注意到在我的 Startup.cs 文件中 ConfigureServices method options.ClaimActions.MapUniqueJsonKey("role","role") 和我的 index.razor 文件中我使用 @context.user.identity.name 它 return 为空。但是当我评论 claimactions 行并将其替换为以下内容时:

options.TokenValidationParameters = new TokenValidationParameters
                    {
                        NameClaimType = "name"
                    };

                    options.UseTokenLifetime = false;

它将 return 当前用户的电子邮件。 当我用上面的代码替换 claimactions.mapuniquejsonkey 行时它只有 return 值的原因是什么? 我试图理解为什么会这样。我在有关 TokenValidationParameters 的文档中看到,但仍然没有像我想的那样理解它。

当我有行 options.ClaimActions.MapUniqueJsonKey("role","role") 时,它 return 为空:

当我注释掉该行并将其替换为上面的两行时:

如果您想查看完整代码,请参见下文:

Startup.cs:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddRazorPages();
            services.AddServerSideBlazor();


            services.AddSingleton(sp => new HttpClient { BaseAddress = new Uri("http://localhost:36626") }); // WebApi project

            services.AddTransient<IWeatherForecastServices, WeatherForecastServices>();

            services.AddAuthentication(options =>
            {
                options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
            })
                .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)

                .AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
                {
                    options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                    options.SignOutScheme = OpenIdConnectDefaults.AuthenticationScheme;

                    options.Authority = "https://localhost:5443"; // IdentityServer Project
                    options.ClientId = "interactive";
                    options.ClientSecret = "KEY";

                    options.ResponseType = "code";
                  
                    options.Scope.Add("profile"); // default scope
                    options.Scope.Add("scope2");
                    options.Scope.Add("roles");
                    options.Scope.Add("permissions");
                    options.Scope.Add("email");
                    options.ClaimActions.MapUniqueJsonKey("role", "role");

                  /*  options.TokenValidationParameters = new TokenValidationParameters
                      {
                         NameClaimType = "name"
                      };

                    options.UseTokenLifetime = false;  */

                    options.SaveTokens = true;
                    options.GetClaimsFromUserInfoEndpoint = true;
                   
                });

            services.AddScoped<TokenProvider>();

            services.AddCors(options =>
            {
                options.AddPolicy("Open", builder => builder.AllowAnyOrigin().AllowAnyHeader());
            }

            );
            
            services.AddAuthorization(options =>
            {
                options.AddPolicy(Policy.Policies.IsUser, 
              Policy.Policies.IsUserPolicy());
            });                     

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseCors("Open");

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapBlazorHub();
                endpoints.MapFallbackToPage("/_Host");
            });
        }

Index.razor:

Index.razor

<AuthorizeView Policy="@Policy.Policies.IsUser">
    <h3>Welcome, <b>@context.User.Identity.Name</b></h3>
    <p>You can only see this if you satisfy the IsUser policy.</p>
</AuthorizeView>

Config.cs 来自 IdentityServer:

                .......
                new Client
                {
                    ClientId = "interactive",
                    ClientSecrets = { new Secret("KEY".Sha256()) },
                    RequirePkce = true,
                    AllowedGrantTypes = GrantTypes.Code,

                    RedirectUris = { "https://localhost:5445/signin-oidc", "https://localhost:44327/signin-oidc" },
                    FrontChannelLogoutUri = "https://localhost:5445/signout-oidc",
                    PostLogoutRedirectUris = { "https://localhost:5445/signout-callback-oidc" },
                    AlwaysIncludeUserClaimsInIdToken = true,
                    AllowOfflineAccess = true,
                    AllowedScopes = { "openid", "profile", "email" , "scope2" ,"weatherforecast-api","roles","permissions"}
                },
            };

The Name claim and the Role claim are mapped to default properties in the ASP.NET Core HTTP context. Sometimes it is required to use different claims for the default properties, or the name claim and the role claim do not match the default values. The claims can be mapped using the TokenValidationParameters property and set to any claim as required.

以下代码片段说明了 TokenValidationParameters 的用法:

  options.TokenValidationParameters = new TokenValidationParameters
   {
     NameClaimType = "email", 
     RoleClaimType = "role"
   };

如您所见,姓名声明映射到“电子邮件”字段,但通常它会映射到“姓名”字段。

注意:由于您的设置请求配置文件范围 (options.Scope.Add("profile");),因此不需要额外的声明映射。也就是说NameClaimType = "name"是多余的

Another way to get the user claims is to use the OpenID Connect User Info API. The ASP.NET Core client application uses the GetClaimsFromUserInfoEndpoint property to configure this. One important difference from the first settings, is that you must specify the claims you require using the MapUniqueJsonKey method, otherwise only the name, given_name and email standard claims will be available in the client application. The claims included in the id_token are mapped per default. This is the major difference to the first option. You must explicitly define some of the claims you require:

   // YOU MUST HAVE THE FIRST LINE WITHOUT WHICH YOU'LL GET NULL VALUES
   options.GetClaimsFromUserInfoEndpoint = true;
   options.ClaimActions.MapUniqueJsonKey("preferred_username", 
                                       "preferred_username");
   options.ClaimActions.MapUniqueJsonKey("gender", "gender");
   options.ClaimActions.MapUniqueJsonKey("role","role");

注意:您应该寻找我和 Brian Parker 的有关身份验证和授权的答案。我们已经在 WebAssembly 和 Blazor Server Apps 中回答了许多问题。这些不是关于该主题的文章,而是我们提供给开发人员的解决方案,它们涵盖了该主题的许多方面,包括自定义声明、声明转换等。Brian Parker 在 Github 中也有完整的应用程序...请参阅它。