在 ASP.NET Core 中使用特定配置來授權

如需 ASP.NET Core 中的驗證配置簡介,請參閱驗證配置

在某些情況下,例如單頁應用程式 (SPA),通常會使用多個驗證方法。 例如,應用程式可能會使用 cookie 型驗證來登入,以及針對 JavaScript 要求使用 JWT 持有人驗證。 在某些情況下,應用程式可能會有多個驗證處理常式執行個體。 例如,兩個 cookie 處理常式,其中一個包含基本身分識別,一個則是在觸發多重要素驗證 (MFA) 時建立的處理常式。 可能會觸發 MFA,因為使用者要求了需要額外安全性的作業。 如需當使用者要求需要 MFA 的資源時,強制執行 MFA 的詳細資訊,請參閱 GitHub 問題使用 MFA 保護一節

驗證配置會在驗證期間設定驗證服務時進行命名。 例如:

using Microsoft.AspNetCore.Authentication;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication()
        .AddCookie(options =>
        {
            options.LoginPath = "/Account/Unauthorized/";
            options.AccessDeniedPath = "/Account/Forbidden/";
        })
        .AddJwtBearer(options =>
        {
            options.Audience = "http://localhost:5001/";
            options.Authority = "http://localhost:5000/";
        });

builder.Services.AddAuthentication()
        .AddIdentityServerJwt();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseHsts();
}

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

app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.MapFallbackToFile("index.html");

app.Run();

在上述程式碼中,已新增兩個驗證處理常式:一個用於 cookie,另一個則用於持有人。

注意

指定預設配置會導致 HttpContext.User 屬性設定為該身分識別。 如果不需要該行為,請叫用 AddAuthentication 的無參數形式來加以停用。

選取具有 Authorize 屬性的配置

在授權點,應用程式會指出要使用的處理常式。 將以逗號分隔的驗證配置清單傳遞至 [Authorize],以選取應用程式將授權的處理常式。 [Authorize] 屬性會指定驗證配置或要使用的配置,不論是否已設定預設值。 例如:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Mvc;

namespace AuthScheme.Controllers;

[Authorize(AuthenticationSchemes = AuthSchemes)]
public class MixedController : Controller
{
    private const string AuthSchemes =
        CookieAuthenticationDefaults.AuthenticationScheme + "," +
        JwtBearerDefaults.AuthenticationScheme;
    public ContentResult Index() => Content(MyWidgets.GetMyContent());

}

在上述範例中,cookie 和持有人處理常式都會執行,並有機會建立及附加目前使用者的身分識別。 只要指定單一配置,對應的處理常式就會執行:

[Authorize(AuthenticationSchemes=JwtBearerDefaults.AuthenticationScheme)]
public class Mixed2Controller : Controller
{
    public ContentResult Index() => Content(MyWidgets.GetMyContent());
}

在上述程式碼中,只會執行具有「Bearer」配置的處理常式。 任何 cookie 型身分識別都會予以忽略。

使用原則選取配置

如果您想要在原則中指定所需的配置,可以在新增原則時設定 AuthenticationSchemes 集合:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("Over18", policy =>
    {
        policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
        policy.RequireAuthenticatedUser();
        policy.Requirements.Add(new MinimumAgeRequirement(18));
    });
});

builder.Services.AddAuthentication()
                .AddIdentityServerJwt();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseHsts();
}

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

app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.MapFallbackToFile("index.html");

app.Run();

在上述範例中,「Over18」原則只會針對「Bearer」處理常式所建立的身分識別執行。 您可以設定 [Authorize] 屬性 (attribute) 的 Policy 屬性 (property) 來使用原則:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace AuthScheme.Controllers;
[Authorize(Policy = "Over18")]
public class RegistrationController : Controller
{
    // Do Registration

使用多個驗證配置

某些應用程式可能需要支援多種驗證類型。 例如,您的應用程式可能會驗證來自 Azure Active Directory 和 users 資料庫的使用者。 另一個範例是應用程式,可驗證來自 Active Directory 同盟服務和 Azure Active Directory B2C 的使用者。 在此情況下,應用程式應該接受數個簽發者提供的 JWT 持有人權杖。

新增您想要接受的所有驗證配置。 例如,下列程式碼會新增兩個具有不同簽發者的 JWT 持有人驗證配置:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;

var builder = WebApplication.CreateBuilder(args);

// Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://localhost:5000/identity/";
        })
        .AddJwtBearer("AzureAD", options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://login.microsoftonline.com/eb971100-7f436/";
        });

// Authorization
builder.Services.AddAuthorization(options =>
{
    var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
        JwtBearerDefaults.AuthenticationScheme,
        "AzureAD");
    defaultAuthorizationPolicyBuilder =
        defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
    options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});

builder.Services.AddAuthentication()
        .AddIdentityServerJwt();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseHsts();
}

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

app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.MapFallbackToFile("index.html");

app.Run();

注意

只會向預設驗證配置 JwtBearerDefaults.AuthenticationScheme 註冊一個 JWT 持有人驗證。 其他驗證必須向唯一的驗證配置進行註冊。

更新預設授權原則以接受這兩種驗證配置。 例如:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;

var builder = WebApplication.CreateBuilder(args);

// Authentication
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://localhost:5000/identity/";
        })
        .AddJwtBearer("AzureAD", options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://login.microsoftonline.com/eb971100-7f436/";
        });

// Authorization
builder.Services.AddAuthorization(options =>
{
    var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
        JwtBearerDefaults.AuthenticationScheme,
        "AzureAD");
    defaultAuthorizationPolicyBuilder =
        defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
    options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});

builder.Services.AddAuthentication()
        .AddIdentityServerJwt();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseHsts();
}

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

app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.MapFallbackToFile("index.html");

app.Run();

覆寫預設授權原則時,可以在控制器中使用 [Authorize] 屬性。 接著,控制器會接受第一或第二個簽發者所發出的 JWT 要求。

請參閱關於使用多個驗證配置的這個 GitHub 問題

下列範例使用 Azure Active Directory B2C 和另一個 Azure Active Directory 租用戶:

using Microsoft.AspNetCore.Authentication;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Net.Http.Headers;
using System.IdentityModel.Tokens.Jwt;

var builder = WebApplication.CreateBuilder(args);

// Authentication
builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = "B2C_OR_AAD";
    options.DefaultChallengeScheme = "B2C_OR_AAD";
})
.AddJwtBearer("B2C", jwtOptions =>
{
    jwtOptions.MetadataAddress = "B2C-MetadataAddress";
    jwtOptions.Authority = "B2C-Authority";
    jwtOptions.Audience = "B2C-Audience";
})
.AddJwtBearer("AAD", jwtOptions =>
{
    jwtOptions.MetadataAddress = "AAD-MetadataAddress";
    jwtOptions.Authority = "AAD-Authority";
    jwtOptions.Audience = "AAD-Audience";
    jwtOptions.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateIssuerSigningKey = true,
        ValidAudiences = builder.Configuration.GetSection("ValidAudiences").Get<string[]>(),
        ValidIssuers = builder.Configuration.GetSection("ValidIssuers").Get<string[]>()
    };
})
.AddPolicyScheme("B2C_OR_AAD", "B2C_OR_AAD", options =>
{
    options.ForwardDefaultSelector = context =>
    {
        string authorization = context.Request.Headers[HeaderNames.Authorization];
        if (!string.IsNullOrEmpty(authorization) && authorization.StartsWith("Bearer "))
        {
            var token = authorization.Substring("Bearer ".Length).Trim();
            var jwtHandler = new JwtSecurityTokenHandler();

            return (jwtHandler.CanReadToken(token) && jwtHandler.ReadJwtToken(token).Issuer.Equals("B2C-Authority"))
                ? "B2C" : "AAD";
        }
        return "AAD";
    };
});

builder.Services.AddAuthentication()
        .AddIdentityServerJwt();

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
else
{
    app.UseHsts();
}

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

app.UseAuthentication();
app.UseIdentityServer();
app.UseAuthorization();

app.MapDefaultControllerRoute().RequireAuthorization();
app.MapRazorPages().RequireAuthorization();

app.MapFallbackToFile("index.html");

app.Run();

在上述程式碼中,ForwardDefaultSelector 是用來選取目前要求的預設配置,驗證處理常式預設應將所有驗證作業轉送至該預設配置。 預設轉送邏輯會先檢查最特定的 ForwardAuthenticateForwardChallengeForwardForbidForwardSignInForwardSignOut 設定,然後檢查 ForwardDefaultSelector,隨後檢查 ForwardDefault。 第一個非 Null 結果會做為要轉送的目標配置。 如需詳細資訊,請參閱 ASP.NET Core 中的原則配置

如需 ASP.NET Core 中的驗證配置簡介,請參閱驗證配置

在某些情況下,例如單頁應用程式 (SPA),通常會使用多個驗證方法。 例如,應用程式可能會使用 cookie 型驗證來登入,以及針對 JavaScript 要求使用 JWT 持有人驗證。 在某些情況下,應用程式可能會有多個驗證處理常式執行個體。 例如,兩個 cookie 處理常式,其中一個包含基本身分識別,一個則是在觸發多重要素驗證 (MFA) 時建立的處理常式。 可能會觸發 MFA,因為使用者要求了需要額外安全性的作業。 如需當使用者要求需要 MFA 的資源時,強制執行 MFA 的詳細資訊,請參閱 GitHub 問題使用 MFA 保護一節

驗證配置會在驗證期間設定驗證服務時進行命名。 例如:

public void ConfigureServices(IServiceCollection services)
{
    // Code omitted for brevity

    services.AddAuthentication()
        .AddCookie(options => {
            options.LoginPath = "/Account/Unauthorized/";
            options.AccessDeniedPath = "/Account/Forbidden/";
        })
        .AddJwtBearer(options => {
            options.Audience = "http://localhost:5001/";
            options.Authority = "http://localhost:5000/";
        });

在上述程式碼中,已新增兩個驗證處理常式:一個用於 cookie,另一個則用於持有人。

注意

指定預設配置會導致 HttpContext.User 屬性設定為該身分識別。 如果不需要該行為,請叫用 AddAuthentication 的無參數形式來加以停用。

選取具有 Authorize 屬性的配置

在授權點,應用程式會指出要使用的處理常式。 將以逗號分隔的驗證配置清單傳遞至 [Authorize],以選取應用程式將授權的處理常式。 [Authorize] 屬性會指定驗證配置或要使用的配置,不論是否已設定預設值。 例如:

[Authorize(AuthenticationSchemes = AuthSchemes)]
public class MixedController : Controller
    // Requires the following imports:
    // using Microsoft.AspNetCore.Authentication.Cookies;
    // using Microsoft.AspNetCore.Authentication.JwtBearer;
    private const string AuthSchemes =
        CookieAuthenticationDefaults.AuthenticationScheme + "," +
        JwtBearerDefaults.AuthenticationScheme;

在上述範例中,cookie 和持有人處理常式都會執行,並有機會建立及附加目前使用者的身分識別。 只要指定單一配置,對應的處理常式就會執行。

[Authorize(AuthenticationSchemes = 
    JwtBearerDefaults.AuthenticationScheme)]
public class MixedController : Controller

在上述程式碼中,只會執行具有「Bearer」配置的處理常式。 任何 cookie 型身分識別都會予以忽略。

使用原則選取配置

如果您想要在原則中指定所需的配置,可以在新增原則時設定 AuthenticationSchemes 集合:

services.AddAuthorization(options =>
{
    options.AddPolicy("Over18", policy =>
    {
        policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
        policy.RequireAuthenticatedUser();
        policy.Requirements.Add(new MinimumAgeRequirement());
    });
});

在上述範例中,「Over18」原則只會針對「Bearer」處理常式所建立的身分識別執行。 您可以設定 [Authorize] 屬性 (attribute) 的 Policy 屬性 (property) 來使用原則:

[Authorize(Policy = "Over18")]
public class RegistrationController : Controller

使用多個驗證配置

某些應用程式可能需要支援多種驗證類型。 例如,您的應用程式可能會驗證來自 Azure Active Directory 和 users 資料庫的使用者。 另一個範例是應用程式,可驗證來自 Active Directory 同盟服務和 Azure Active Directory B2C 的使用者。 在此情況下,應用程式應該接受數個簽發者提供的 JWT 持有人權杖。

新增您想要接受的所有驗證配置。 例如,Startup.ConfigureServices 中的下列程式碼會新增兩個具有不同簽發者的 JWT 持有人驗證配置:

public void ConfigureServices(IServiceCollection services)
{
    // Code omitted for brevity

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://localhost:5000/identity/";
        })
        .AddJwtBearer("AzureAD", options =>
        {
            options.Audience = "https://localhost:5000/";
            options.Authority = "https://login.microsoftonline.com/eb971100-6f99-4bdc-8611-1bc8edd7f436/";
        });
}

注意

只會向預設驗證配置 JwtBearerDefaults.AuthenticationScheme 註冊一個 JWT 持有人驗證。 其他驗證必須向唯一的驗證配置進行註冊。

下一個步驟是更新預設授權原則以接受這兩種驗證配置。 例如:

public void ConfigureServices(IServiceCollection services)
{
    // Code omitted for brevity

    services.AddAuthorization(options =>
    {
        var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
            JwtBearerDefaults.AuthenticationScheme,
            "AzureAD");
        defaultAuthorizationPolicyBuilder = 
            defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
        options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
    });
}

覆寫預設授權原則時,可以在控制器中使用 [Authorize] 屬性。 接著,控制器會接受第一或第二個簽發者所發出的 JWT 要求。

請參閱關於使用多個驗證配置的這個 GitHub 問題