共用方式為


ASP.NET Core 中的對應、自訂和轉換宣告

作者 Damien Bowden

宣告可以從任何使用者或身分識別數據建立,這些數據可以使用受信任的識別提供者或 ASP.NET 核心身分識別來發行。 宣告是成對的名稱和數值,代表主體的身分,而不是主體可以執行的動作。 本文涵蓋下列區域:

  • 如何使用 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 方法來指定所需的宣告,否則用戶端應用程式中只能使用 namegiven_nameemail 標準宣告。 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 沒有值或角色遺失,請檢查傳回宣告中的值,並設定 NameClaimTypeRoleClaimType 值。 從用戶端驗證傳回的宣告可以在 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 中保存外部提供者的其他宣告和權杖

宣告可以從任何使用者或身分識別數據建立,這些數據可以使用受信任的識別提供者或 ASP.NET 核心身分識別來發行。 宣告是成對的名稱和數值,代表主體的身分,而不是主體可以執行的動作。 本文涵蓋下列區域:

  • 如何使用 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 方法來指定所需的宣告,否則用戶端應用程式中只能使用 namegiven_nameemail 標準宣告。 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 沒有值或角色遺失,請檢查傳回宣告中的值,並設定 NameClaimTypeRoleClaimType 值。 從用戶端驗證傳回的宣告可以在 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 中擴充或新增自訂宣告

請參閱下列文件:

將宣告新增至 Identity 使用 IUserClaimsPrincipalFactory

從外部身份提供者映射宣稱

請參閱下列文件:

在 ASP.NET Core 中保存外部提供者的其他宣告和權杖