【问题标题】:Override AuthorizeAttribute in ASP.Net Core and respond Json status在 ASP.Net Core 中覆盖 AuthorizeAttribute 并响应 Json 状态
【发布时间】:2017-01-30 22:34:38
【问题描述】:

我正在从 ASP.Net Framework 迁移到 ASP.Net Core。

在带有 Web API 2 项目的 ASP.Net Framework 中,我可以像这样自定义 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 中相同的功能?

谢谢,

【问题讨论】:

    标签: asp.net-core asp.net-core-webapi


    【解决方案1】:

    您的处理方法不正确。从未真正鼓励为此编写自定义属性或扩展现有属性。 ASP.NET Core 角色仍然是系统的一部分,以实现向后兼容性,但现在也不鼓励使用。

    有一个很棒的 2 部分系列文章,介绍了一些驱动架构的变化以及这些变化的使用方式和应该使用的方式 here。如果您仍想依赖角色,您可以这样做,但我建议您使用策略。

    要连接策略,请执行以下操作:

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

    其他资源

    【讨论】:

    • 如果我想解析 HttpRequest 以从 header 中获取 access_token 怎么办?因为我想自己实现 GitHub OAuth、Google OAuth 和 Facebook OAuth。第三方库使用起来很复杂。
    • 不是问题,设置此代码并检查AuthorizationHandlerContext context 变量。你会发现它包含HttpRequest,带有headers和routedata等。
    • 实际上,我在那里看不到 HttpRequest。这是我的图片:mediafire.com/view/c1bv86ktb16qqjj/Screenshot_%2815%29.png
    • 抱歉,我遗漏了一个重要细节。你需要投这个context as ActionContext,然后你可以投这个context.HttpContext.Request
    • 铸造抛出异常:无法通过引用转换、装箱转换、拆箱转换、包装转换将类型“Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext”转换为“Microsoft.AspNetCore.Mvc.ActionContext”,或空类型转换Core..NETCoreApp,Version=v1.0
    【解决方案2】:

    我的评论看起来很糟糕,所以我发布了一个答案,但只有在你使用 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 可能会很好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-01-08
      • 2018-08-15
      • 1970-01-01
      • 1970-01-01
      • 2011-12-21
      • 2011-11-30
      • 1970-01-01
      • 2015-01-12
      相关资源
      最近更新 更多