在ASP.Net Core中覆盖AuthorizeAttribute并响应Json状态

8

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

在ASP.Net Framework中的Web 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中实现与之相同的功能?
谢谢。
2个回答

25
你的方法不正确。从来没有真正鼓励为此编写自定义属性或扩展现有属性。在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;
    }
}

额外资源


没问题,设置这段代码并检查 AuthorizationHandlerContext context 变量。你会发现它包含了带有头部和路由数据等的 HttpRequest - David Pine
实际上,我在那里没有看到HttpRequest。这是我的图片:http://www.mediafire.com/view/c1bv86ktb16qqjj/Screenshot_%2815%29.png - Redplane
强制类型转换抛出异常:无法通过引用转换、装箱转换、拆箱转换、包装转换或空类型转换将类型“Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext”转换为“Microsoft.AspNetCore.Mvc.ActionContext” Core..NETCoreApp,Version=v1.0。 - Redplane
哎呀,我应该在仅凭记忆做出假设之前查看我的源代码。这是你想要的:context.Resource as FilterContext。其中 contextAuthorizationHandlerContext - David Pine
这个解决方案给了我这个结果;“未指定身份验证方案,并且找不到默认的挑战方案。” 所以我需要更多地搜索和学习新的mvc核心,忘记我的实际业务项目,真好。 - Lost_In_Library
显示剩余3条评论

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接