web-dev-qa-db-ja.com

ASP.Net CoreのAuthorizeAttributeをオーバーライドし、Jsonステータスに応答します

ASP.NetFrameworkからASP.NetCoreに移行しています。

Web API2プロジェクトを使用するASP.NetFrameworkでは、次のように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と同じことをどのように達成できますか?

ありがとうございました、

5
Redplane

あなたはこれに間違ってアプローチしています。このためのカスタム属性を作成したり、既存の属性を拡張したりすることは、実際には推奨されませんでした。 ASP.NET Coreを使用すると、下位互換性のために役割はシステムの一部になりますが、現在は推奨されていません。

いくつかの駆動アーキテクチャの変更と、これがどのように利用されるべきかについての素晴らしい2部構成のシリーズがあります ここ 。それでもロールに依存したい場合はそうすることができますが、ポリシーを使用することをお勧めします。

ポリシーを配線するには、次のようにします。

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

追加リソース

17
David Pine

私のコメントはコメントとして見栄えが悪いので、回答を投稿しますが、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)); }) 

また、質問コードの「HandleRequirementAsync」署名にはasyncキーワードが不要であることに気付きました。そして、Task.CompletedTaskを返すのは良いことだと思います。

1
barbara.post