Поделиться через


Авторизация на основе ресурсов в ASP.NET Core

Подход к авторизации зависит от ресурса. Например, только автор документа может обновить документ. Следовательно, документ необходимо извлечь из хранилища данных, прежде чем будет выполняться оценка авторизации.

Оценка атрибутов возникает перед привязкой данных и перед выполнением обработчика страниц или действия, загружающего документ. По этим причинам декларативная авторизация с атрибутом [Authorize] недостаточно. Вместо этого можно вызвать пользовательский метод авторизации — стиль, известный как императивная авторизация.

Просмотреть или скачать пример кода (описание скачивания).

Создание приложения ASP.NET Core с пользовательскими данными, защищенными авторизацией , содержит пример приложения, использующего авторизацию на основе ресурсов.

Использование императивной авторизации

Авторизация реализуется как IAuthorizationService услуга и регистрируется в коллекции служб при запуске приложения. Служба становится доступной с помощью внедрения зависимостей в обработчики страниц или действия.

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

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

IAuthorizationService имеет два AuthorizeAsync перегрузки метода: одно принимает ресурс и имя политики, а другое принимает ресурс и список требований для оценки.

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

В следующем примере ресурс для защиты загружается в пользовательский Document объект. AuthorizeAsync Вызывается перегрузка, чтобы определить, разрешен ли текущий пользователь изменять предоставленный документ. Настраиваемая политика авторизации EditPolicy учитывается в решении. Дополнительные сведения о создании политик авторизации см. в статье "Авторизация на основе пользовательских политик".

Примечание.

В следующих примерах кода предполагается, что проверка подлинности выполняется и задает User свойство.

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

Создание обработчика на основе ресурсов

Написание обработчика для авторизации на основе ресурсов не отличается от написания обработчика простых требований. Создайте пользовательский класс требований и реализуйте класс обработчика требований. Дополнительные сведения о создании класса требований см. в разделе "Требования".

Класс обработчика указывает как требование, так и тип ресурса. Например, обработчик, используюющий ресурс SameAuthorRequirement , 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 { }

В предыдущем примере представьте, что SameAuthorRequirement это особый случай более универсального SpecificAuthorRequirement класса. Класс SpecificAuthorRequirement (не показан) содержит Name свойство, представляющее имя автора. Свойство Name может быть задано текущему пользователю.

Зарегистрируйте требование и обработчик в 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>();

Требования к операционной деятельности

Если вы принимаете решения на основе результатов операций CRUD (Создание, чтение, обновление, удаление), используйте вспомогательный OperationAuthorizationRequirement класс. Этот класс позволяет создавать один обработчик вместо отдельного класса для каждого типа операции. Чтобы использовать его, укажите некоторые имена операций:

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

Обработчик реализуется следующим образом, используя OperationAuthorizationRequirement требование и Document ресурс:

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

Предыдущий обработчик проверяет операцию с помощью ресурса, пользователя identityи свойства требования Name .

Проблема и запрет с помощью обработчика операционных ресурсов

В этом разделе показано, как обрабатываются результаты сложного и запрета действий, а также как проблемы и запретить их.

Чтобы вызвать обработчик операционных ресурсов, укажите операцию при вызове AuthorizeAsync в обработчике страниц или действии. В следующем примере определяется, разрешен ли прошедший проверку подлинности пользователь просматривать предоставленный документ.

Примечание.

В следующих примерах кода предполагается, что проверка подлинности выполняется и задает User свойство.

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

Если авторизация выполнена успешно, возвращается страница просмотра документа. Если авторизация завершается ошибкой, но пользователь проходит проверку подлинности, возвращает ForbidResult все программы промежуточного слоя проверки подлинности, что авторизация завершилась ошибкой. Возвращается ChallengeResult при выполнении проверки подлинности. Для клиентов интерактивного браузера может потребоваться перенаправить пользователя на страницу входа.

Подход к авторизации зависит от ресурса. Например, только автор документа может обновить документ. Следовательно, документ необходимо извлечь из хранилища данных, прежде чем будет выполняться оценка авторизации.

Оценка атрибутов возникает перед привязкой данных и перед выполнением обработчика страниц или действия, загружающего документ. По этим причинам декларативная авторизация с атрибутом [Authorize] недостаточно. Вместо этого можно вызвать пользовательский метод авторизации — стиль, известный как императивная авторизация.

Просмотреть или скачать пример кода (описание скачивания).

Создание приложения ASP.NET Core с пользовательскими данными, защищенными авторизацией , содержит пример приложения, использующего авторизацию на основе ресурсов.

Использование императивной авторизации

Авторизация реализуется как IAuthorizationService услуга и регистрируется в коллекции служб в Startup классе. Служба становится доступной с помощью внедрения зависимостей в обработчики страниц или действия.

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

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

IAuthorizationService имеет два AuthorizeAsync перегрузки метода: одно принимает ресурс и имя политики, а другое принимает ресурс и список требований для оценки.

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

В следующем примере ресурс для защиты загружается в пользовательский Document объект. AuthorizeAsync Вызывается перегрузка, чтобы определить, разрешен ли текущий пользователь изменять предоставленный документ. Настраиваемая политика авторизации EditPolicy учитывается в решении. Дополнительные сведения о создании политик авторизации см. в статье "Авторизация на основе пользовательских политик".

Примечание.

В следующих примерах кода предполагается, что проверка подлинности выполняется и задает User свойство.

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

Создание обработчика на основе ресурсов

Написание обработчика для авторизации на основе ресурсов не отличается от написания обработчика простых требований. Создайте пользовательский класс требований и реализуйте класс обработчика требований. Дополнительные сведения о создании класса требований см. в разделе "Требования".

Класс обработчика указывает как требование, так и тип ресурса. Например, обработчик, используюющий ресурс SameAuthorRequirement , 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 { }

В предыдущем примере представьте, что SameAuthorRequirement это особый случай более универсального SpecificAuthorRequirement класса. Класс SpecificAuthorRequirement (не показан) содержит Name свойство, представляющее имя автора. Свойство Name может быть задано текущему пользователю.

Зарегистрируйте требование и обработчик в 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>();

Требования к операционной деятельности

Если вы принимаете решения на основе результатов операций CRUD (Создание, чтение, обновление, удаление), используйте вспомогательный OperationAuthorizationRequirement класс. Этот класс позволяет создавать один обработчик вместо отдельного класса для каждого типа операции. Чтобы использовать его, укажите некоторые имена операций:

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

Обработчик реализуется следующим образом, используя OperationAuthorizationRequirement требование и Document ресурс:

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

Предыдущий обработчик проверяет операцию с помощью ресурса, пользователя identityи свойства требования Name .

Проблема и запрет с помощью обработчика операционных ресурсов

В этом разделе показано, как обрабатываются результаты сложного и запрета действий, а также как проблемы и запретить их.

Чтобы вызвать обработчик операционных ресурсов, укажите операцию при вызове AuthorizeAsync в обработчике страниц или действии. В следующем примере определяется, разрешен ли прошедший проверку подлинности пользователь просматривать предоставленный документ.

Примечание.

В следующих примерах кода предполагается, что проверка подлинности выполняется и задает User свойство.

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

Если авторизация выполнена успешно, возвращается страница просмотра документа. Если авторизация завершается ошибкой, но пользователь проходит проверку подлинности, возвращает ForbidResult все программы промежуточного слоя проверки подлинности, что авторизация завершилась ошибкой. Возвращается ChallengeResult при выполнении проверки подлинности. Для клиентов интерактивного браузера может потребоваться перенаправить пользователя на страницу входа.

Подход к авторизации зависит от ресурса. Например, только автор документа может обновить документ. Следовательно, документ необходимо извлечь из хранилища данных, прежде чем будет выполняться оценка авторизации.

Оценка атрибутов возникает перед привязкой данных и перед выполнением обработчика страниц или действия, загружающего документ. По этим причинам декларативная авторизация с атрибутом [Authorize] недостаточно. Вместо этого можно вызвать пользовательский метод авторизации — стиль, известный как императивная авторизация.

Просмотреть или скачать пример кода (описание скачивания).

Создание приложения ASP.NET Core с пользовательскими данными, защищенными авторизацией , содержит пример приложения, использующего авторизацию на основе ресурсов.

Использование императивной авторизации

Авторизация реализуется как IAuthorizationService услуга и регистрируется в коллекции служб в Startup классе. Служба становится доступной с помощью внедрения зависимостей в обработчики страниц или действия.

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

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

IAuthorizationService имеет два AuthorizeAsync перегрузки метода: одно принимает ресурс и имя политики, а другое принимает ресурс и список требований для оценки.

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

В следующем примере ресурс для защиты загружается в пользовательский Document объект. AuthorizeAsync Вызывается перегрузка, чтобы определить, разрешен ли текущий пользователь изменять предоставленный документ. Настраиваемая политика авторизации EditPolicy учитывается в решении. Дополнительные сведения о создании политик авторизации см. в статье "Авторизация на основе пользовательских политик".

Примечание.

В следующих примерах кода предполагается, что проверка подлинности выполняется и задает User свойство.

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

Создание обработчика на основе ресурсов

Написание обработчика для авторизации на основе ресурсов не отличается от написания обработчика простых требований. Создайте пользовательский класс требований и реализуйте класс обработчика требований. Дополнительные сведения о создании класса требований см. в разделе "Требования".

Класс обработчика указывает как требование, так и тип ресурса. Например, обработчик, используюющий ресурс SameAuthorRequirement , 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 { }

В предыдущем примере представьте, что SameAuthorRequirement это особый случай более универсального SpecificAuthorRequirement класса. Класс SpecificAuthorRequirement (не показан) содержит Name свойство, представляющее имя автора. Свойство Name может быть задано текущему пользователю.

Зарегистрируйте требование и обработчик в 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>();

Требования к операционной деятельности

Если вы принимаете решения на основе результатов операций CRUD (Создание, чтение, обновление, удаление), используйте вспомогательный OperationAuthorizationRequirement класс. Этот класс позволяет создавать один обработчик вместо отдельного класса для каждого типа операции. Чтобы использовать его, укажите некоторые имена операций:

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

Обработчик реализуется следующим образом, используя OperationAuthorizationRequirement требование и Document ресурс:

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

Предыдущий обработчик проверяет операцию с помощью ресурса, пользователя identityи свойства требования Name .

Проблема и запрет с помощью обработчика операционных ресурсов

В этом разделе показано, как обрабатываются результаты сложного и запрета действий, а также как проблемы и запретить их.

Чтобы вызвать обработчик операционных ресурсов, укажите операцию при вызове AuthorizeAsync в обработчике страниц или действии. В следующем примере определяется, разрешен ли прошедший проверку подлинности пользователь просматривать предоставленный документ.

Примечание.

В следующих примерах кода предполагается, что проверка подлинности выполняется и задает User свойство.

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

Если авторизация выполнена успешно, возвращается страница просмотра документа. Если авторизация завершается ошибкой, но пользователь проходит проверку подлинности, возвращает ForbidResult все программы промежуточного слоя проверки подлинности, что авторизация завершилась ошибкой. Возвращается ChallengeResult при выполнении проверки подлинности. Для клиентов интерактивного браузера может потребоваться перенаправить пользователя на страницу входа.