你可以根据任何用户或标识数据创建声明,并使用受信任的标识提供者或 ASP.NET Core 标识发出声明。 声明是一个名称值对,表示主体是什么,而不是主体可以做什么。 本文涵盖以下几个方面:
- 如何使用 OpenID Connect 客户端配置和映射声明
- 设置名称和角色声明
- 重置声明命名空间
- 使用 TransformAsync 自定义、扩展声明
使用 OpenID Connect 身份验证映射声明
用户信息声明可以在 id_token 中返回,后者在身份验证成功后返回。 ASP.NET Core 客户端应用只需要配置文件范围。 使用 id_token 进行声明操作时,不需要进行额外的声明映射。
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.Scope.Add("profile");
       options.SaveTokens = true;
   });
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
上述代码需要 Microsoft.AspNetCore.Authentication.OpenIdConnect NuGet 包。
获取用户声明的另一种方法是使用 OpenID Connect 用户信息 API。 ASP.NET Core 客户端应用使用 GetClaimsFromUserInfoEndpoint 属性配置此设置。 它与第一个设置的重要区别在于,必须使用 MapUniqueJsonKey 方法指定所需的声明,否则客户端应用中只有 name、given_name 和 email 标准声明可用。 id_token 中包含的声明按默认值进行映射。 这是与第一个选项的主要区别所在。 您需明确定义您所需的部分声明。
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.Scope.Add("profile");
       options.SaveTokens = true;
       options.GetClaimsFromUserInfoEndpoint = true;
       options.ClaimActions.MapUniqueJsonKey("preferred_username",
                                             "preferred_username");
       options.ClaimActions.MapUniqueJsonKey("gender", "gender");
   });
var app = builder.Build();
// Code removed for brevity.
注意
如果标识提供者的发现文档宣称支持 PAR,则默认的 Open ID Connect 处理程序将使用推送的授权请求 (PAR)。 标识提供者的发现文档通常位于 .well-known/openid-configuration。 如果无法在标识提供者的客户端配置中使用 PAR,则可以使用 PushedAuthorizationBehavior 选项禁用 PAR。
builder.Services
    .AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie()
    .AddOpenIdConnect("oidc", oidcOptions =>
    {
        // Other provider-specific configuration goes here.
        // The default value is PushedAuthorizationBehavior.UseIfAvailable.
        // 'OpenIdConnectOptions' does not contain a definition for 'PushedAuthorizationBehavior'
        // and no accessible extension method 'PushedAuthorizationBehavior' accepting a first argument
        // of type 'OpenIdConnectOptions' could be found
        oidcOptions.PushedAuthorizationBehavior = PushedAuthorizationBehavior.Disable;
    });
若要确保仅在使用 PAR 时身份验证成功,请改用 PushedAuthorizationBehavior.Require。 此更改还引入了一个新的 OnPushAuthorization 事件来 OpenIdConnectEvents,可用于自定义推送的授权请求或手动处理它。 有关详细信息,请参阅 API 建议。
名称声明和角色声明映射
名称声明和角色声明映射到 ASP.NET Core HTTP 上下文中的默认属性。 有时需要对默认属性使用不同的声明,否则,名称声明和角色声明与默认值不匹配。 可以使用 TokenValidationParameters 属性映射声明,并根据需要将其设置为任何声明。 声明中的值可以直接在 HttpContext User.Identity.Name 属性和角色中使用。
如果 User.Identity.Name 没有值或缺少角色,请检查返回的声明中的值并设置 NameClaimType 和 RoleClaimType 值。 可以在 HTTP 上下文中查看客户端身份验证返回的声明。
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
  .AddCookie()
  .AddOpenIdConnect(options =>
  {
       // Other options...
       options.TokenValidationParameters = new TokenValidationParameters
       {
          NameClaimType = "email"
          //, RoleClaimType = "role"
       };
  });
声明命名空间、默认命名空间
ASP.NET Core 添加了一些已知声明的默认命名空间,应用中可能不需要这些命名空间。 你可以选择禁用这些添加的命名空间并使用 OpenID Connect 服务器创建的确切声明。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
JsonWebTokenHandler.DefaultInboundClaimTypeMap.Clear();
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.Scope.Add("profile");
       options.SaveTokens = true;
   });
var app = builder.Build();
// Code removed for brevity.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.Scope.Add("profile");
       options.SaveTokens = true;
   });
var app = builder.Build();
// Code removed for brevity.
如果需要按方案禁用命名空间而不是进行全局禁用,则可以使用“MapInboundClaims = false”选项。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.MapInboundClaims = false;
       options.Scope.Add("profile");
       options.SaveTokens = true;
   });
var app = builder.Build();
// Code removed for brevity.
使用 IClaimsTransformation 扩展或添加自定义声明
IClaimsTransformation 接口可用于向 ClaimsPrincipal 类添加额外的声明。 该接口需要一个 TransformAsync 方法。 此方法可能会被多次调用。 仅当 ClaimsPrincipal 中没有时才添加新声明。 系统将创建 ClaimsIdentity 以添加新声明,并且可以将其添加到 ClaimsPrincipal 中。
using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;
public class MyClaimsTransformation : IClaimsTransformation
{
    public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        ClaimsIdentity claimsIdentity = new ClaimsIdentity();
        var claimType = "myNewClaim";
        if (!principal.HasClaim(claim => claim.Type == claimType))
        {
            claimsIdentity.AddClaim(new Claim(claimType, "myClaimValue"));
        }
        principal.AddIdentity(claimsIdentity);
        return Task.FromResult(principal);
    }
}
IClaimsTransformation 接口和 MyClaimsTransformation 类可以注册为服务:
builder.Services.AddTransient<IClaimsTransformation, MyClaimsTransformation>();
映射来自外部标识提供者的声明
请参阅以下文档:
你可以根据任何用户或标识数据创建声明,并使用受信任的标识提供者或 ASP.NET Core 标识发出声明。 声明是一个名称值对,表示主体是什么,而不是主体可以做什么。 本文涵盖以下几个方面:
- 如何使用 OpenID Connect 客户端配置和映射声明
- 设置名称和角色声明
- 重置声明命名空间
- 使用 TransformAsync 自定义、扩展声明
使用 OpenID Connect 身份验证映射声明
配置文件声明可以在 id_token 中返回,后者在身份验证成功后返回。 ASP.NET Core 客户端应用只需要配置文件范围。 使用 id_token 进行声明时,不需要额外的声明映射。
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.Scope.Add("profile");
       options.SaveTokens = true;
   });
获取用户声明的另一种方法是使用 OpenID Connect 用户信息 API。 ASP.NET Core 客户端应用程序使用 GetClaimsFromUserInfoEndpoint 属性配置此设置。 它与第一个设置的重要区别在于,你必须使用 MapUniqueJsonKey 方法指定所需的声明,否则客户端应用程序中只有 name、given_name 和 email 标准声明可用。 id_token 中包含的声明按默认值进行映射。 这是与第一个选项的主要区别所在。 你必须对某些所需的声明进行显式定义。
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       options.SignInScheme = "Cookies";
       options.Authority = "-your-identity-provider-";
       options.RequireHttpsMetadata = true;
       options.ClientId = "-your-clientid-";
       options.ClientSecret = "-your-client-secret-from-user-secrets-or-keyvault";
       options.ResponseType = "code";
       options.UsePkce = true;
       options.Scope.Add("profile");
       options.SaveTokens = true;
       options.GetClaimsFromUserInfoEndpoint = true;
       options.ClaimActions.MapUniqueJsonKey("preferred_username", "preferred_username");
       options.ClaimActions.MapUniqueJsonKey("gender", "gender");
   }); 
名称声明和角色声明映射
名称声明和角色声明映射到 ASP.NET Core HTTP 上下文中的默认属性。 有时需要对默认属性使用不同的声明,否则,名称声明和角色声明与默认值不匹配。 可以使用 TokenValidationParameters 属性映射声明,并根据需要将其设置为任何声明。 声明中的值可以直接在 HttpContext User.Identity.Name 属性和角色中使用。
如果 User.Identity.Name 没有值或缺少角色,请检查返回的声明中的值并设置 NameClaimType 和 RoleClaimType 值。 可以在 HTTP 上下文中查看客户端身份验证返回的声明。
public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
   .AddCookie()
   .AddOpenIdConnect(options =>
   {
       // other options...
       options.TokenValidationParameters = new TokenValidationParameters
       {
         NameClaimType = "email", 
         // RoleClaimType = "role"
       };
   });
声明命名空间、默认命名空间
ASP.NET Core 添加了一些已知声明的默认命名空间,应用中可能不需要这些命名空间。 你可以选择禁用这些添加的命名空间并使用 OpenID Connect 服务器创建的确切声明。
public void Configure(IApplicationBuilder app)
{
    JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
使用 IClaimsTransformation 扩展或添加自定义声明
IClaimsTransformation 接口可用于向 ClaimsPrincipal 类添加额外的声明。 该接口需要一个 TransformAsync 方法。 此方法可能会被多次调用。 仅当 ClaimsPrincipal 中没有时才添加新声明。 系统将创建 ClaimsIdentity 以添加新声明,并且可以将其添加到 ClaimsPrincipal 中。
public class MyClaimsTransformation : IClaimsTransformation
{
    public Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
       ClaimsIdentity claimsIdentity = new ClaimsIdentity();
       var claimType = "myNewClaim";
       if (!principal.HasClaim(claim => claim.Type == claimType))
       {		   
          claimsIdentity.AddClaim(new Claim(claimType, "myClaimValue"));
       }
       principal.AddIdentity(claimsIdentity);
       return Task.FromResult(principal);
    }
}
IClaimsTransformation 接口和 MyClaimsTransformation 类可以作为服务添加到 ConfigureServices 方法中。
public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IClaimsTransformation, MyClaimsTransformation>();
在 ASP.NET Core 中扩展或添加自定义声明Identity
请参阅以下文档:
使用 IUserClaimsPrincipalFactory 将声明添加到 Identity
映射来自外部标识提供者的声明
请参阅以下文档: