Autoryzacja oparta na zasobach w usłudze ASP.NET Core

Podejście autoryzacji zależy od zasobu. Na przykład tylko autor dokumentu jest autoryzowany do aktualizowania dokumentu. W związku z tym należy pobrać dokument z magazynu danych przed rozpoczęciem oceny autoryzacji.

Ocena atrybutu występuje przed powiązaniem danych i przed wykonaniem procedury obsługi strony lub akcji, która ładuje dokument. Z tych powodów autoryzacja deklaratywna z atrybutem [Authorize] nie wystarczy. Zamiast tego można wywołać niestandardową metodę autoryzacji — styl znany jako autoryzacja imperatywne.

Wyświetl lub pobierz przykładowy kod (jak pobrać).

Tworzenie aplikacji ASP.NET Core z danymi użytkownika chronionymi przez autoryzację zawiera przykładową aplikację korzystającą z autoryzacji opartej na zasobach.

Używanie autoryzacji imperatywnej

Autoryzacja IAuthorizationService jest implementowana jako usługa i jest zarejestrowana w kolekcji usług podczas uruchamiania aplikacji. Usługa jest udostępniana za pośrednictwem wstrzykiwania zależności do procedur obsługi stron lub akcji.

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IDocumentRepository _documentRepository;

    public DocumentController(IAuthorizationService authorizationService,
                              IDocumentRepository documentRepository)
    {
        _authorizationService = authorizationService;
        _documentRepository = documentRepository;
    }

IAuthorizationService ma dwa AuthorizeAsync przeciążenia metody: jeden akceptujący zasób i nazwę zasad oraz drugą akceptującą zasób oraz listę wymagań do oceny.

Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          string policyName);

W poniższym przykładzie zasób do zabezpieczenia jest ładowany do obiektu niestandardowego Document . Wywoływane AuthorizeAsync jest przeciążenie w celu określenia, czy bieżący użytkownik może edytować podany dokument. Niestandardowe zasady autoryzacji "EditPolicy" są uwzględniane w decyzji. Zobacz Autoryzacja oparta na zasadach niestandardowych, aby uzyskać więcej informacji na temat tworzenia zasad autoryzacji.

Uwaga

W poniższych przykładach kodu przyjęto założenie, że uwierzytelnianie zostało uruchomione i ustawiono User właściwość .

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, "EditPolicy");

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

Pisanie procedury obsługi opartej na zasobach

Pisanie procedury obsługi dla autoryzacji opartej na zasobach nie różni się znacznie od pisania procedury obsługi zwykłych wymagań. Utwórz niestandardową klasę wymagań i zaimplementuj klasę obsługi wymagań. Aby uzyskać więcej informacji na temat tworzenia klasy wymagań, zobacz Wymagania.

Klasa obsługi określa zarówno wymaganie, jak i typ zasobu. Na przykład procedura obsługi korzystająca z elementu SameAuthorRequirement i zasobu jest następująca Document :

public class DocumentAuthorizationHandler : 
    AuthorizationHandler<SameAuthorRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   SameAuthorRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

W poprzednim przykładzie wyobraź sobie, że SameAuthorRequirement jest to szczególny przypadek bardziej ogólnej SpecificAuthorRequirement klasy. Klasa SpecificAuthorRequirement (nie pokazana) zawiera właściwość reprezentującą Name nazwę autora. Właściwość Name może być ustawiona na bieżącego użytkownika.

Zarejestruj wymaganie i procedurę obsługi w programie Program.cs:

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

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("EditPolicy", policy =>
        policy.Requirements.Add(new SameAuthorRequirement()));
});

builder.Services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
builder.Services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
builder.Services.AddScoped<IDocumentRepository, DocumentRepository>();

Wymagania operacyjne

Jeśli podejmujesz decyzje na podstawie wyników operacji CRUD (tworzenie, odczytywanie, aktualizowanie, usuwanie), użyj OperationAuthorizationRequirement klasy pomocnika. Ta klasa umożliwia pisanie pojedynczej procedury obsługi zamiast pojedynczej klasy dla każdego typu operacji. Aby go użyć, podaj nazwy operacji:

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

Procedura obsługi jest implementowana w następujący sposób, korzystając z OperationAuthorizationRequirement wymagania i Document zasobu:

public class DocumentAuthorizationCrudHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author &&
            requirement.Name == Operations.Read.Name)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Poprzednia procedura obsługi weryfikuje operację przy użyciu zasobu, tożsamości użytkownika i właściwości wymagania Name .

Wyzwanie i zakaz obsługi zasobów operacyjnych

W tej sekcji pokazano, jak wyzwanie i zabronić wyników akcji są przetwarzane oraz jak wyzwanie i zabraniają różnicy.

Aby wywołać program obsługi zasobów operacyjnych, określ operację podczas wywoływania AuthorizeAsync w programie obsługi lub akcji strony. Poniższy przykład określa, czy uwierzytelniony użytkownik może wyświetlać podany dokument.

Uwaga

W poniższych przykładach kodu przyjęto założenie, że uwierzytelnianie zostało uruchomione i ustawiono User właściwość .

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, Operations.Read);

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

Jeśli autoryzacja powiedzie się, zostanie zwrócona strona przeglądania dokumentu. Jeśli autoryzacja zakończy się niepowodzeniem, ale użytkownik jest uwierzytelniony, zwraca informację ForbidResult o tym, że autoryzacja nie powiodła się. Element jest ChallengeResult zwracany, gdy należy przeprowadzić uwierzytelnianie. W przypadku klientów przeglądarki interakcyjnej może być odpowiednie przekierowanie użytkownika do strony logowania.

Podejście autoryzacji zależy od zasobu. Na przykład tylko autor dokumentu jest autoryzowany do aktualizowania dokumentu. W związku z tym należy pobrać dokument z magazynu danych przed rozpoczęciem oceny autoryzacji.

Ocena atrybutu występuje przed powiązaniem danych i przed wykonaniem procedury obsługi strony lub akcji, która ładuje dokument. Z tych powodów autoryzacja deklaratywna z atrybutem [Authorize] nie wystarczy. Zamiast tego można wywołać niestandardową metodę autoryzacji — styl znany jako autoryzacja imperatywne.

Wyświetl lub pobierz przykładowy kod (jak pobrać).

Tworzenie aplikacji ASP.NET Core z danymi użytkownika chronionymi przez autoryzację zawiera przykładową aplikację korzystającą z autoryzacji opartej na zasobach.

Używanie autoryzacji imperatywnej

Autoryzacja IAuthorizationService jest implementowana jako usługa i jest zarejestrowana w kolekcji usług w Startup klasie . Usługa jest udostępniana za pośrednictwem wstrzykiwania zależności do procedur obsługi stron lub akcji.

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IDocumentRepository _documentRepository;

    public DocumentController(IAuthorizationService authorizationService,
                              IDocumentRepository documentRepository)
    {
        _authorizationService = authorizationService;
        _documentRepository = documentRepository;
    }

IAuthorizationService ma dwa AuthorizeAsync przeciążenia metody: jeden akceptujący zasób i nazwę zasad oraz drugą akceptującą zasób oraz listę wymagań do oceny.

Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          string policyName);

W poniższym przykładzie zasób do zabezpieczenia jest ładowany do obiektu niestandardowego Document . Wywoływane AuthorizeAsync jest przeciążenie w celu określenia, czy bieżący użytkownik może edytować podany dokument. Niestandardowe zasady autoryzacji "EditPolicy" są uwzględniane w decyzji. Zobacz Autoryzacja oparta na zasadach niestandardowych, aby uzyskać więcej informacji na temat tworzenia zasad autoryzacji.

Uwaga

W poniższych przykładach kodu przyjęto założenie, że uwierzytelnianie zostało uruchomione i ustawiono User właściwość .

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, "EditPolicy");

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

Pisanie procedury obsługi opartej na zasobach

Pisanie procedury obsługi dla autoryzacji opartej na zasobach nie różni się znacznie od pisania procedury obsługi zwykłych wymagań. Utwórz niestandardową klasę wymagań i zaimplementuj klasę obsługi wymagań. Aby uzyskać więcej informacji na temat tworzenia klasy wymagań, zobacz Wymagania.

Klasa obsługi określa zarówno wymaganie, jak i typ zasobu. Na przykład procedura obsługi korzystająca z elementu SameAuthorRequirement i zasobu jest następująca Document :

public class DocumentAuthorizationHandler : 
    AuthorizationHandler<SameAuthorRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   SameAuthorRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

W poprzednim przykładzie wyobraź sobie, że SameAuthorRequirement jest to szczególny przypadek bardziej ogólnej SpecificAuthorRequirement klasy. Klasa SpecificAuthorRequirement (nie pokazana) zawiera właściwość reprezentującą Name nazwę autora. Właściwość Name może być ustawiona na bieżącego użytkownika.

Zarejestruj wymaganie i procedurę obsługi w programie Startup.ConfigureServices:

services.AddControllersWithViews();
services.AddRazorPages();

services.AddAuthorization(options =>
{
    options.AddPolicy("EditPolicy", policy =>
        policy.Requirements.Add(new SameAuthorRequirement()));
});

services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
services.AddScoped<IDocumentRepository, DocumentRepository>();

Wymagania operacyjne

Jeśli podejmujesz decyzje na podstawie wyników operacji CRUD (tworzenie, odczytywanie, aktualizowanie, usuwanie), użyj OperationAuthorizationRequirement klasy pomocnika. Ta klasa umożliwia pisanie pojedynczej procedury obsługi zamiast pojedynczej klasy dla każdego typu operacji. Aby go użyć, podaj nazwy operacji:

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

Procedura obsługi jest implementowana w następujący sposób, korzystając z OperationAuthorizationRequirement wymagania i Document zasobu:

public class DocumentAuthorizationCrudHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author &&
            requirement.Name == Operations.Read.Name)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Poprzednia procedura obsługi weryfikuje operację przy użyciu zasobu, tożsamości użytkownika i właściwości wymagania Name .

Wyzwanie i zakaz obsługi zasobów operacyjnych

W tej sekcji pokazano, jak wyzwanie i zabronić wyników akcji są przetwarzane oraz jak wyzwanie i zabraniają różnicy.

Aby wywołać program obsługi zasobów operacyjnych, określ operację podczas wywoływania AuthorizeAsync w programie obsługi lub akcji strony. Poniższy przykład określa, czy uwierzytelniony użytkownik może wyświetlać podany dokument.

Uwaga

W poniższych przykładach kodu przyjęto założenie, że uwierzytelnianie zostało uruchomione i ustawiono User właściwość .

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, Operations.Read);

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

Jeśli autoryzacja powiedzie się, zostanie zwrócona strona przeglądania dokumentu. Jeśli autoryzacja zakończy się niepowodzeniem, ale użytkownik jest uwierzytelniony, zwraca informację ForbidResult o tym, że autoryzacja nie powiodła się. Element jest ChallengeResult zwracany, gdy należy przeprowadzić uwierzytelnianie. W przypadku klientów przeglądarki interakcyjnej może być odpowiednie przekierowanie użytkownika do strony logowania.

Podejście autoryzacji zależy od zasobu. Na przykład tylko autor dokumentu jest autoryzowany do aktualizowania dokumentu. W związku z tym należy pobrać dokument z magazynu danych przed rozpoczęciem oceny autoryzacji.

Ocena atrybutu występuje przed powiązaniem danych i przed wykonaniem procedury obsługi strony lub akcji, która ładuje dokument. Z tych powodów autoryzacja deklaratywna z atrybutem [Authorize] nie wystarczy. Zamiast tego można wywołać niestandardową metodę autoryzacji — styl znany jako autoryzacja imperatywne.

Wyświetl lub pobierz przykładowy kod (jak pobrać).

Tworzenie aplikacji ASP.NET Core z danymi użytkownika chronionymi przez autoryzację zawiera przykładową aplikację korzystającą z autoryzacji opartej na zasobach.

Używanie autoryzacji imperatywnej

Autoryzacja IAuthorizationService jest implementowana jako usługa i jest zarejestrowana w kolekcji usług w Startup klasie . Usługa jest udostępniana za pośrednictwem wstrzykiwania zależności do procedur obsługi stron lub akcji.

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IDocumentRepository _documentRepository;

    public DocumentController(IAuthorizationService authorizationService,
                              IDocumentRepository documentRepository)
    {
        _authorizationService = authorizationService;
        _documentRepository = documentRepository;
    }

IAuthorizationService ma dwa AuthorizeAsync przeciążenia metody: jeden akceptujący zasób i nazwę zasad oraz drugą akceptującą zasób oraz listę wymagań do oceny.

Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          string policyName);

W poniższym przykładzie zasób do zabezpieczenia jest ładowany do obiektu niestandardowego Document . Wywoływane AuthorizeAsync jest przeciążenie w celu określenia, czy bieżący użytkownik może edytować podany dokument. Niestandardowe zasady autoryzacji "EditPolicy" są uwzględniane w decyzji. Zobacz Autoryzacja oparta na zasadach niestandardowych, aby uzyskać więcej informacji na temat tworzenia zasad autoryzacji.

Uwaga

W poniższych przykładach kodu przyjęto założenie, że uwierzytelnianie zostało uruchomione i ustawiono User właściwość .

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, "EditPolicy");

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

Pisanie procedury obsługi opartej na zasobach

Pisanie procedury obsługi dla autoryzacji opartej na zasobach nie różni się znacznie od pisania procedury obsługi zwykłych wymagań. Utwórz niestandardową klasę wymagań i zaimplementuj klasę obsługi wymagań. Aby uzyskać więcej informacji na temat tworzenia klasy wymagań, zobacz Wymagania.

Klasa obsługi określa zarówno wymaganie, jak i typ zasobu. Na przykład procedura obsługi korzystająca z elementu SameAuthorRequirement i zasobu jest następująca Document :

public class DocumentAuthorizationHandler : 
    AuthorizationHandler<SameAuthorRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   SameAuthorRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

W poprzednim przykładzie wyobraź sobie, że SameAuthorRequirement jest to szczególny przypadek bardziej ogólnej SpecificAuthorRequirement klasy. Klasa SpecificAuthorRequirement (nie pokazana) zawiera właściwość reprezentującą Name nazwę autora. Właściwość Name może być ustawiona na bieżącego użytkownika.

Zarejestruj wymaganie i procedurę obsługi w programie Startup.ConfigureServices:

services.AddMvc();

services.AddAuthorization(options =>
{
    options.AddPolicy("EditPolicy", policy =>
        policy.Requirements.Add(new SameAuthorRequirement()));
});

services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
services.AddScoped<IDocumentRepository, DocumentRepository>();

Wymagania operacyjne

Jeśli podejmujesz decyzje na podstawie wyników operacji CRUD (tworzenie, odczytywanie, aktualizowanie, usuwanie), użyj OperationAuthorizationRequirement klasy pomocnika. Ta klasa umożliwia pisanie pojedynczej procedury obsługi zamiast pojedynczej klasy dla każdego typu operacji. Aby go użyć, podaj nazwy operacji:

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

Procedura obsługi jest implementowana w następujący sposób, korzystając z OperationAuthorizationRequirement wymagania i Document zasobu:

public class DocumentAuthorizationCrudHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author &&
            requirement.Name == Operations.Read.Name)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Poprzednia procedura obsługi weryfikuje operację przy użyciu zasobu, tożsamości użytkownika i właściwości wymagania Name .

Wyzwanie i zakaz obsługi zasobów operacyjnych

W tej sekcji pokazano, jak wyzwanie i zabronić wyników akcji są przetwarzane oraz jak wyzwanie i zabraniają różnicy.

Aby wywołać program obsługi zasobów operacyjnych, określ operację podczas wywoływania AuthorizeAsync w programie obsługi lub akcji strony. Poniższy przykład określa, czy uwierzytelniony użytkownik może wyświetlać podany dokument.

Uwaga

W poniższych przykładach kodu przyjęto założenie, że uwierzytelnianie zostało uruchomione i ustawiono User właściwość .

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, Operations.Read);

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

Jeśli autoryzacja powiedzie się, zostanie zwrócona strona przeglądania dokumentu. Jeśli autoryzacja zakończy się niepowodzeniem, ale użytkownik jest uwierzytelniony, zwraca informację ForbidResult o tym, że autoryzacja nie powiodła się. Element jest ChallengeResult zwracany, gdy należy przeprowadzić uwierzytelnianie. W przypadku klientów przeglądarki interakcyjnej może być odpowiednie przekierowanie użytkownika do strony logowania.