ASP.NET Core 中的對應、自訂和轉換宣告
宣告可以從任何使用者或身分識別數據建立,這些數據可以使用受信任的識別提供者或 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
方法來指定所需的宣告,否則用戶端應用程式中只能使用 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 核心身分識別來發行。 宣告是成對的名稱和數值,代表主體的身分,而不是主體可以執行的動作。 本文涵蓋下列區域:
- 如何使用 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 中擴充或新增自訂宣告
請參閱下列文件:
將宣告新增至 Identity 使用 IUserClaimsPrincipalFactory
從外部身份提供者映射宣稱
請參閱下列文件: