Autoryzacja przy użyciu określonego schematu na platformie ASP.NET Core

Aby zapoznać się z wprowadzeniem do schematów uwierzytelniania w programie ASP.NET Core, zobacz Schemat uwierzytelniania.

W niektórych scenariuszach, takich jak aplikacje jednostronicowe (SPA), często używa się wielu metod uwierzytelniania. Na przykład aplikacja może używać cookieuwierzytelniania opartego na logowaniu i uwierzytelnianiu elementu nośnego JWT dla żądań języka JavaScript. W niektórych przypadkach aplikacja może mieć wiele wystąpień programu obsługi uwierzytelniania. Na przykład dwa cookie programy obsługi, w których jeden zawiera tożsamość podstawową, i jeden jest tworzony po wyzwoleniu uwierzytelniania wieloskładnikowego (MFA). Uwierzytelnianie wieloskładnikowe może zostać wyzwolone, ponieważ użytkownik zażądał operacji wymagającej dodatkowych zabezpieczeń. Aby uzyskać więcej informacji na temat wymuszania uwierzytelniania wieloskładnikowego, gdy użytkownik żąda zasobu wymagającego uwierzytelniania wieloskładnikowego, zobacz sekcję GitHub issue Protect with MFA (Ochrona problemów z usługą GitHub za pomocą uwierzytelniania wieloskładnikowego).

Schemat uwierzytelniania jest nazwany, gdy usługa uwierzytelniania jest skonfigurowana podczas uwierzytelniania. Przykład:

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();

W poprzednim kodzie dodano dwa programy obsługi uwierzytelniania: jeden dla cookies i jeden dla elementu nośnego.

Uwaga

Określenie schematu domyślnego HttpContext.User powoduje ustawienie właściwości na tej tożsamości. Jeśli to zachowanie nie jest pożądane, wyłącz je, wywołując bez parametrów AddAuthenticationpostać .

Wybieranie schematu z atrybutem Authorize

W momencie autoryzacji aplikacja wskazuje procedurę obsługi do użycia. Wybierz procedurę obsługi, za pomocą której aplikacja będzie autoryzowana, przekazując rozdzielaną przecinkami listę schematów uwierzytelniania do elementu [Authorize]. Atrybut [Authorize] określa schemat uwierzytelniania lub schematy do użycia niezależnie od tego, czy jest skonfigurowany domyślny. Przykład:

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());

}

W poprzednim przykładzie zarówno programy obsługi elementu nośnego, jak cookie i mają szansę utworzyć i dołączyć tożsamość dla bieżącego użytkownika. Określając tylko pojedynczy schemat, odpowiedni program obsługi jest uruchamiany:

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

W poprzednim kodzie jest uruchamiany tylko program obsługi ze schematem "Bearer". Wszystkie cookietożsamości oparte na tożsamościach są ignorowane.

Wybieranie schematu z zasadami

Jeśli wolisz określić żądane schematy w zasadach, możesz ustawić AuthenticationSchemes kolekcję podczas dodawania zasad:

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();

W poprzednim przykładzie zasady "Over18" są uruchamiane tylko względem tożsamości utworzonej przez program obsługi "Bearer". Użyj zasad, ustawiając właściwość atrybutu [Authorize]Policy :

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

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

Używanie wielu schematów uwierzytelniania

Niektóre aplikacje mogą wymagać obsługi wielu typów uwierzytelniania. Na przykład aplikacja może uwierzytelniać użytkowników z usługi Azure Active Directory i z bazy danych użytkowników. Innym przykładem jest aplikacja, która uwierzytelnia użytkowników zarówno z usług Active Directory Federation Services, jak i z usługi Azure Active Directory B2C. W takim przypadku aplikacja powinna zaakceptować token elementu nośnego JWT od kilku wystawców.

Dodaj wszystkie schematy uwierzytelniania, które chcesz zaakceptować. Na przykład poniższy kod dodaje dwa schematy uwierzytelniania elementu nośnego JWT z różnymi wystawcami:

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();

Uwaga

Tylko jedno uwierzytelnianie elementu nośnego JWT jest rejestrowane przy użyciu domyślnego schematu JwtBearerDefaults.AuthenticationSchemeuwierzytelniania. Dodatkowe uwierzytelnianie musi zostać zarejestrowane przy użyciu unikatowego schematu uwierzytelniania.

Zaktualizuj domyślne zasady autoryzacji, aby akceptowały oba schematy uwierzytelniania. Przykład:

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();

Ponieważ domyślne zasady autoryzacji są zastępowane, można użyć atrybutu [Authorize] w kontrolerach. Następnie kontroler akceptuje żądania z JWT wystawione przez pierwszego lub drugiego wystawcę.

Zobacz ten problem z usługą GitHub dotyczący korzystania z wielu schematów uwierzytelniania.

W poniższym przykładzie użyto usługi Azure Active Directory B2C i innej dzierżawy usługi 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();

W poprzednim kodzie służy do wybierania schematu domyślnego dla bieżącego żądania, ForwardDefaultSelector do którego programy obsługi uwierzytelniania powinny domyślnie przekazywać wszystkie operacje uwierzytelniania. Domyślna logika przekazywania sprawdza najpierw najbardziej specyficzne ForwardAuthenticateustawienia , , ForwardChallengeForwardForbid, ForwardSignIni ForwardSignOut , a następnie sprawdzając ForwardDefaultSelector, a następnie ForwardDefault. Pierwszy wynik bez wartości null jest używany jako schemat docelowy do przekazania dalej. Aby uzyskać więcej informacji, zobacz Schematy zasad w programie ASP.NET Core.

Aby zapoznać się z wprowadzeniem do schematów uwierzytelniania w programie ASP.NET Core, zobacz Schemat uwierzytelniania.

W niektórych scenariuszach, takich jak aplikacje jednostronicowe (SPA), często używa się wielu metod uwierzytelniania. Na przykład aplikacja może używać cookieuwierzytelniania opartego na logowaniu i uwierzytelnianiu elementu nośnego JWT dla żądań języka JavaScript. W niektórych przypadkach aplikacja może mieć wiele wystąpień programu obsługi uwierzytelniania. Na przykład dwa cookie programy obsługi, w których jeden zawiera tożsamość podstawową, i jeden jest tworzony po wyzwoleniu uwierzytelniania wieloskładnikowego (MFA). Uwierzytelnianie wieloskładnikowe może zostać wyzwolone, ponieważ użytkownik zażądał operacji wymagającej dodatkowych zabezpieczeń. Aby uzyskać więcej informacji na temat wymuszania uwierzytelniania wieloskładnikowego, gdy użytkownik żąda zasobu wymagającego uwierzytelniania wieloskładnikowego, zobacz sekcję GitHub issue Protect with MFA (Ochrona problemów z usługą GitHub za pomocą uwierzytelniania wieloskładnikowego).

Schemat uwierzytelniania jest nazwany, gdy usługa uwierzytelniania jest skonfigurowana podczas uwierzytelniania. Przykład:

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/";
        });

W poprzednim kodzie dodano dwa programy obsługi uwierzytelniania: jeden dla cookies i jeden dla elementu nośnego.

Uwaga

Określenie schematu domyślnego HttpContext.User powoduje ustawienie właściwości na tej tożsamości. Jeśli to zachowanie nie jest pożądane, wyłącz je, wywołując bez parametrów AddAuthenticationpostać .

Wybieranie schematu z atrybutem Authorize

W momencie autoryzacji aplikacja wskazuje procedurę obsługi do użycia. Wybierz procedurę obsługi, za pomocą której aplikacja będzie autoryzowana, przekazując rozdzielaną przecinkami listę schematów uwierzytelniania do elementu [Authorize]. Atrybut [Authorize] określa schemat uwierzytelniania lub schematy do użycia niezależnie od tego, czy jest skonfigurowany domyślny. Przykład:

[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;

W poprzednim przykładzie zarówno programy obsługi elementu nośnego, jak cookie i mają szansę utworzyć i dołączyć tożsamość dla bieżącego użytkownika. Określając tylko pojedynczy schemat, odpowiedni program obsługi jest uruchamiany.

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

W poprzednim kodzie jest uruchamiany tylko program obsługi ze schematem "Bearer". Wszystkie cookietożsamości oparte na tożsamościach są ignorowane.

Wybieranie schematu z zasadami

Jeśli wolisz określić żądane schematy w zasadach, możesz ustawić kolekcję podczas dodawania AuthenticationSchemes zasad:

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

W poprzednim przykładzie zasady "Over18" są uruchamiane tylko względem tożsamości utworzonej przez program obsługi "Bearer". Użyj zasad, ustawiając właściwość atrybutu [Authorize]Policy :

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

Używanie wielu schematów uwierzytelniania

Niektóre aplikacje mogą wymagać obsługi wielu typów uwierzytelniania. Na przykład aplikacja może uwierzytelniać użytkowników z usługi Azure Active Directory i z bazy danych użytkowników. Innym przykładem jest aplikacja, która uwierzytelnia użytkowników zarówno z usług Active Directory Federation Services, jak i z usługi Azure Active Directory B2C. W takim przypadku aplikacja powinna zaakceptować token elementu nośnego JWT od kilku wystawców.

Dodaj wszystkie schematy uwierzytelniania, które chcesz zaakceptować. Na przykład poniższy kod w pliku Startup.ConfigureServices dodaje dwa schematy uwierzytelniania elementu nośnego JWT z różnymi wystawcami:

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/";
        });
}

Uwaga

Tylko jedno uwierzytelnianie elementu nośnego JWT jest rejestrowane przy użyciu domyślnego schematu JwtBearerDefaults.AuthenticationSchemeuwierzytelniania. Dodatkowe uwierzytelnianie musi zostać zarejestrowane przy użyciu unikatowego schematu uwierzytelniania.

Następnym krokiem jest zaktualizowanie domyślnych zasad autoryzacji w celu akceptowania obu schematów uwierzytelniania. Przykład:

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();
    });
}

Ponieważ domyślne zasady autoryzacji są zastępowane, można użyć atrybutu [Authorize] w kontrolerach. Następnie kontroler akceptuje żądania z JWT wystawione przez pierwszego lub drugiego wystawcę.

Zobacz ten problem z usługą GitHub dotyczący korzystania z wielu schematów uwierzytelniania.