Autoryzacja oparta na zasobach w ASP.NET CoreResource-based authorization in ASP.NET Core

Strategia autoryzacji zależy od zasobów, do których uzyskuje się dostęp.Authorization strategy depends upon the resource being accessed. Rozważ dokument, który ma właściwość Author.Consider a document that has an author property. Tylko autor może zaktualizować dokument.Only the author is allowed to update the document. W związku z tym dokument musi zostać pobrany z magazynu danych, zanim będzie można przeprowadzić ocenę autoryzacji.Consequently, the document must be retrieved from the data store before authorization evaluation can occur.

Obliczanie atrybutu występuje przed powiązaniem danych i przed wykonaniem procedury obsługi stron lub akcji ładującej dokument.Attribute evaluation occurs before data binding and before execution of the page handler or action that loads the document. Z tych powodów niewystarczająca jest autoryzacja deklaratywna z [Authorize] atrybutem.For these reasons, declarative authorization with an [Authorize] attribute doesn't suffice. Zamiast tego można wywołać niestandardową metodę autoryzacji — stylu znanej jako samodzielna autoryzacja .Instead, you can invoke a custom authorization method—a style known as imperative authorization .

Tworzenie aplikacji ASP.NET Core przy użyciu danych użytkownika chronionych przez autoryzację zawiera przykładową aplikację, która korzysta z autoryzacji opartej na zasobach.Create an ASP.NET Core app with user data protected by authorization contains a sample app that uses resource-based authorization.

Używanie bezwzględnej autoryzacjiUse imperative authorization

Autoryzacja jest zaimplementowana jako usługa IAuthorizationService i jest zarejestrowana w kolekcji usług w Startup klasie.Authorization is implemented as an IAuthorizationService service and is registered in the service collection within the Startup class. Usługa jest udostępniana za pośrednictwem iniekcji zależności do obsługi stron lub akcji.The service is made available via dependency injection to page handlers or actions.

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

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

IAuthorizationService ma dwie AuthorizeAsync metody przeciążenia: jeden akceptujący zasób oraz nazwę zasad i inne zaakceptowanie zasobu oraz listę wymagań do obliczenia.IAuthorizationService has two AuthorizeAsync method overloads: one accepting the resource and the policy name and the other accepting the resource and a list of requirements to evaluate.

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

W poniższym przykładzie zasób do zabezpieczenia jest ładowany do niestandardowego Document obiektu.In the following example, the resource to be secured is loaded into a custom Document object. AuthorizeAsyncZostanie wywołana metoda przeciążenia, aby określić, czy bieżący użytkownik może edytować podany dokument.An AuthorizeAsync overload is invoked to determine whether the current user is allowed to edit the provided document. Niestandardowe zasady autoryzacji "EditPolicy" są uwzględniane w decyzji.A custom "EditPolicy" authorization policy is factored into the decision. Aby uzyskać więcej informacji na temat tworzenia zasad autoryzacji, zobacz niestandardową autoryzację opartą na zasadach .See Custom policy-based authorization for more on creating authorization policies.

Uwaga

W poniższych przykładach kodu przyjęto założenie, że uwierzytelnianie zostało uruchomione i ustawione User Właściwość.The following code samples assume authentication has run and set the User property.

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();
    }
}
[HttpGet]
public async Task<IActionResult> Edit(Guid documentId)
{
    Document document = _documentRepository.Find(documentId);

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

    if (await _authorizationService
        .AuthorizeAsync(User, document, "EditPolicy"))
    {
        return View(document);
    }
    else
    {
        return new ChallengeResult();
    }
}

Napisz procedurę obsługi opartą na zasobachWrite a resource-based handler

Pisanie procedury obsługi autoryzacji opartej na zasobach nie jest znacznie inne niż pisanie procedury obsługi zwykłego wymagania.Writing a handler for resource-based authorization isn't much different than writing a plain requirements handler. Utwórz niestandardową klasę wymagania i zaimplementuj klasę obsługi wymagań.Create a custom requirement class, and implement a requirement handler class. Aby uzyskać więcej informacji na temat tworzenia klasy wymagań, zobacz wymagania.For more information on creating a requirement class, see Requirements.

Klasa obsługi określa typ wymagania i zasobu.The handler class specifies both the requirement and resource type. Na przykład program obsługi wykorzystujący SameAuthorRequirement a i zasób jest Document następujący:For example, a handler utilizing a SameAuthorRequirement and a Document resource follows:

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

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

W poprzednim przykładzie Załóżmy, że SameAuthorRequirement jest szczególnym przypadkiem bardziej generycznej SpecificAuthorRequirement klasy.In the preceding example, imagine that SameAuthorRequirement is a special case of a more generic SpecificAuthorRequirement class. SpecificAuthorRequirementKlasa (niepokazywana) zawiera Name Właściwość reprezentującą nazwę autora.The SpecificAuthorRequirement class (not shown) contains a Name property representing the name of the author. NameWłaściwość może zostać ustawiona na bieżącego użytkownika.The Name property could be set to the current user.

Rejestrowanie wymagania i obsługi w programie Startup.ConfigureServices :Register the requirement and handler in 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>();
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>();
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 operacyjneOperational requirements

Jeśli podejmujesz decyzje w oparciu o wyniki operacji CRUD (tworzenie, odczytywanie, aktualizowanie, usuwanie), użyj klasy pomocnika OperationAuthorizationRequirement .If you're making decisions based on the outcomes of CRUD (Create, Read, Update, Delete) operations, use the OperationAuthorizationRequirement helper class. Ta klasa umożliwia pisanie pojedynczej procedury obsługi zamiast pojedynczej klasy dla każdego typu operacji.This class enables you to write a single handler instead of an individual class for each operation type. Aby go użyć, Podaj nazwy niektórych operacji:To use it, provide some operation names:

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 przy użyciu OperationAuthorizationRequirement wymagania i Document zasobu:The handler is implemented as follows, using an OperationAuthorizationRequirement requirement and a Document resource:

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

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

Poprzednia procedura obsługi sprawdza poprawność operacji przy użyciu zasobu, tożsamości użytkownika i jego Name właściwości.The preceding handler validates the operation using the resource, the user's identity, and the requirement's Name property.

Wyzwania i Zabroń przy użyciu obsługi zasobów operacyjnychChallenge and forbid with an operational resource handler

W tej sekcji pokazano, jak są przetwarzane wyniki akcji wezwanie i zabraniające działania oraz jak wyzwania i zabraniają się.This section shows how the challenge and forbid action results are processed and how challenge and forbid differ.

Aby wywołać procedurę obsługi zasobów operacyjnych, określ operację podczas wywoływania AuthorizeAsync w obsłudze stron lub akcji.To call an operational resource handler, specify the operation when invoking AuthorizeAsync in your page handler or action. Poniższy przykład określa, czy uwierzytelniony użytkownik może wyświetlić podany dokument.The following example determines whether the authenticated user is permitted to view the provided document.

Uwaga

W poniższych przykładach kodu przyjęto założenie, że uwierzytelnianie zostało uruchomione i ustawione User Właściwość.The following code samples assume authentication has run and set the User property.

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 do wyświetlania dokumentu.If authorization succeeds, the page for viewing the document is returned. Jeśli autoryzacja nie powiedzie się, ale użytkownik zostanie uwierzytelniony, zwraca ForbidResult informację o tym, że uwierzytelnianie nie powiodło się.If authorization fails but the user is authenticated, returning ForbidResult informs any authentication middleware that authorization failed. ChallengeResultJest zwracany, gdy należy przeprowadzić uwierzytelnianie.A ChallengeResult is returned when authentication must be performed. W przypadku klientów interakcyjnej przeglądarki może być konieczne przekierowanie użytkownika do strony logowania.For interactive browser clients, it may be appropriate to redirect the user to a login page.

[HttpGet]
public async Task<IActionResult> View(Guid documentId)
{
    Document document = _documentRepository.Find(documentId);

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

    if (await _authorizationService
        .AuthorizeAsync(User, document, Operations.Read))
    {
        return View(document);
    }
    else
    {
        return new ChallengeResult();
    }
}

Jeśli autoryzacja powiedzie się, zostanie zwrócony widok dla dokumentu.If authorization succeeds, the view for the document is returned. Jeśli autoryzacja nie powiedzie się, zwraca ChallengeResult informację o tym, że uwierzytelnianie pośredniczące nie powiodło się, a oprogramowanie pośredniczące może pobrać odpowiednią odpowiedź.If authorization fails, returning ChallengeResult informs any authentication middleware that authorization failed, and the middleware can take the appropriate response. Odpowiednia odpowiedź może zwrócić kod stanu 401 lub 403.An appropriate response could be returning a 401 or 403 status code. W przypadku klientów interakcyjnych przeglądarki może to oznaczać przekierowanie użytkownika do strony logowania.For interactive browser clients, it could mean redirecting the user to a login page.