Переопределить AuthorizeAttribute в ASP.Net Core и ответить на статус Json

Я перехожу с ASP.Net Framework на ASP.Net Core.

В проекте ASP.Net Framework с веб-API 2 я могу настроить AuthorizeAttribute следующим образом:

public class ApiAuthorizeAttribute : AuthorizationFilterAttribute
{
    #region Methods

    /// <summary>
    ///     Override authorization event to do custom authorization.
    /// </summary>
    /// <param name="httpActionContext"></param>
    public override void OnAuthorization(HttpActionContext httpActionContext)
    {
        // Retrieve email and password.
        var accountEmail =
            httpActionContext.Request.Headers.Where(
                    x =>
                        !string.IsNullOrEmpty(x.Key) &&
                        x.Key.Equals("Email"))
                .Select(x => x.Value.FirstOrDefault())
                .FirstOrDefault();

        // Retrieve account password.
        var accountPassword =
            httpActionContext.Request.Headers.Where(
                    x =>
                        !string.IsNullOrEmpty(x.Key) &&
                        x.Key.Equals("Password"))
                .Select(x => x.Value.FirstOrDefault()).FirstOrDefault();

        // Account view model construction.
        var filterAccountViewModel = new FilterAccountViewModel();
        filterAccountViewModel.Email = accountEmail;
        filterAccountViewModel.Password = accountPassword;
        filterAccountViewModel.EmailComparision = TextComparision.Equal;
        filterAccountViewModel.PasswordComparision = TextComparision.Equal;

        // Find the account.
        var account = RepositoryAccount.FindAccount(filterAccountViewModel);

        // Account is not found.
        if (account == null)
        {
            // Treat the account as unthorized.
            httpActionContext.Response = httpActionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);

            return;
        }

        // Role is not defined which means the request is allowed.
        if (_roles == null)
            return;

        // Role is not allowed 
        if (!_roles.Any(x => x == account.Role))
        {
            // Treat the account as unthorized.
            httpActionContext.Response = httpActionContext.Request.CreateResponse(HttpStatusCode.Forbidden);

            return;
        }

        // Store the requester information in action argument.
        httpActionContext.ActionArguments["Account"] = account;
    }

    #endregion

    #region Properties

    /// <summary>
    ///     Repository which provides function to access account database.
    /// </summary>
    public IRepositoryAccount RepositoryAccount { get; set; }

    /// <summary>
    ///     Which role can be allowed to access server.
    /// </summary>
    private readonly byte[] _roles;

    #endregion

    #region Constructor

    /// <summary>
    ///     Initialize instance with default settings.
    /// </summary>
    public ApiAuthorizeAttribute()
    {
    }

    /// <summary>
    ///     Initialize instance with allowed role.
    /// </summary>
    /// <param name="roles"></param>
    public ApiAuthorizeAttribute(byte[] roles)
    {
        _roles = roles;
    }

    #endregion
}

В моем настроенном AuthorizeAttribute я могу проверить, действительна ли учетная запись, и вернуть HttpStatusCode с сообщением клиенту.

Я пытаюсь сделать то же самое в ASP.Net Core, но не могу переопределить OnAuthorization.

Как я могу добиться того же, что и в ASP.Net Framework?

Спасибо,


person Redplane    schedule 22.09.2016    source источник


Ответы (2)


Вы неправильно к этому подходите. На самом деле никогда не поощрялось писать для этого пользовательские атрибуты или расширять существующие. В ASP.NET Core роли по-прежнему отделены от системы для обеспечения обратной совместимости, но теперь они также не рекомендуются.

Существует отличная серия из двух частей о некоторых основных изменениях архитектуры и способах их использования, которые можно найти здесь. Если вы хотите по-прежнему полагаться на роли, вы можете это сделать, но я бы предложил использовать политики.

Чтобы связать политику, выполните следующие действия:

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthorization(options =>
    {
        options.AddPolicy(nameof(Policy.Account), 
                          policy => policy.Requirements.Add(new AccountRequirement()));
    });

    services.AddSingleton<IAuthorizationHandler, AccountHandler>();
}

Для удобства я создал перечисление Policy.

public enum Policy { Account };

Оформите точки входа следующим образом:

[
    HttpPost,
    Authorize(Policy = nameof(Policy.Account))
]
public async Task<IActionResult> PostSomething([FromRoute] blah)
{
}

AccountRequirement — это просто заполнитель, он должен реализовать интерфейс IAuthorizationRequirement.

public class AccountRequirement: IAuthorizationRequirement { }

Теперь нам просто нужно создать обработчик и подключить его к DI.

public class AccountHandler : AuthorizationHandler<AccountRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        AccountRequirement requirement)
    {
        // Your logic here... or anything else you need to do.
        if (context.User.IsInRole("fooBar"))
        {
            // Call 'Succeed' to mark current requirement as passed
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Дополнительные ресурсы

person David Pine    schedule 22.09.2016
comment
Что делать, если я хочу проанализировать HttpRequest, чтобы получить access_token из заголовка? Потому что я хочу реализовать GitHub OAuth, Google OAuth и Facebook OAuth самостоятельно. Сторонние библиотеки сложны в использовании. - person Redplane; 22.09.2016
comment
Не проблема, настройте этот код и проверьте переменную AuthorizationHandlerContext context. Вы обнаружите, что он содержит HttpRequest с заголовками, маршрутными данными и т. д. - person David Pine; 22.09.2016
comment
На самом деле я не вижу там HttpRequest. Вот мое изображение: mediafire.com/view/c1bv86ktb16qqjj/Screenshot_%2815 %29.png - person Redplane; 22.09.2016
comment
Извините, я упустил важную деталь. Вам нужно разыграть это context as ActionContext, тогда вы можете сделать это context.HttpContext.Request. - person David Pine; 22.09.2016
comment
Кастинг выдает мне исключение: невозможно преобразовать тип «Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext» в «Microsoft.AspNetCore.Mvc.ActionContext» посредством преобразования ссылки, преобразования упаковки, преобразования распаковки, преобразования преобразования или преобразования нулевого типа Core..NETCoreApp , Версия = v1.0 - person Redplane; 22.09.2016
comment
Тьфу, я действительно должен посмотреть на мой исходный код, прежде чем делать предположения, основанные только на воспоминаниях. Вот что тебе нужно, context.Resource as FilterContext. Где context это AuthorizationHandlerContext. - person David Pine; 22.09.2016
comment
Это решение дает мне этот результат; Схема аутентификации не была указана, и схема DefaultChallengeScheme не была найдена. Поэтому мне нужно все больше и больше искать и изучать новое ядро ​​​​mvc и забыть о моем реальном бизнес-проекте, хорошо. - person Lost_In_Library; 19.08.2017
comment
Мне помог этот пример: github.com/aspnet/AuthSamples/tree/master/samples / - person barbara.post; 04.09.2018

Мой комментарий выглядит плохо, как комментарий, поэтому я публикую ответ, но он полезен только в том случае, если вы используете MVC:

// don't forget this 
services.AddSingleton<IAuthorizationHandler, MyCustomAuthorizationHandler>();
services
   .AddMvc(config => { var policy = new AuthorizationPolicyBuilder() 
      .RequireAuthenticatedUser() .AddRequirements(new[] { new MyCustomRequirement() }) 
       .Build(); config.Filters.Add(new AuthorizeFilter(policy)); }) 

Я также заметил, что ключевое слово async является лишним для подписи HandleRequirementAsync в коде вопроса. И я думаю, что возвращение Task.CompletedTask может быть хорошим.

person barbara.post    schedule 04.09.2018