ASP.NET Core中如何定制AuthorizeAttribute

在ASP.NET中通过重写bool AuthorizeCore(HttpContextBase httpContext)方法就可以定制AuthorizeAttribute,但ASP.NET Core中它已经不存在了。那么ASP.NET Core要怎么实现相同的功能呢?

ASP.Net Core 团队推荐的方法是使用策略(policy)设计来实现相同中的功能。新方法背后的基本思想是使用新的 [Authorize] 属性来指定“策略”(例如 [Authorize(Policy = "YouNeedToBe18ToDoThis")],其中策略在应用程序的 Startup.cs 中注册,它会执行一段代码(即确保用户符合年龄要求,年龄18岁或以上才能访问)。

策略设计是对框架的一个很好的补充,ASP.Net 安全核心团队的引入值得表扬。这种方法的缺点是它无法为最常见的简单断言给定控制器或动作需要给定声明类型的需求提供方便的解决方案。如果应用程序可能有数百个独立的权限来管理资源(“CanCreateOrder”、“CanReadOrder”、“CanUpdateOrder”、“CanDeleteOrder”等)的 CRUD 操作,新方法要么需要重复的一对一策略名称和声明名称之间的一种映射(例如 options.AddPolicy("CanUpdateOrder", policy => policy.RequireClaim(MyClaimTypes.Permission, "CanUpdateOrder));),或者编写一些代码来在运行时执行这些注册(例如,从数据库中读取所有声明类型并在循环中执行上述调用)在大多数情况下,这种方法的导致了不必要的开销。

虽然 ASP.Net Core 安全团队建议永远不要创建自己的解决方案,但在某些情况下,这可能是不错的选择。

以下是一个实现,它使用 IAuthorizationFilter 提供一种简单的方法来表达给定控制器或操作的声明要求:

public class ClaimRequirementAttribute : TypeFilterAttribute
{
    public ClaimRequirementAttribute(string claimType, string claimValue) : base(typeof(ClaimRequirementFilter))
    {
        Arguments = new object[] {new Claim(claimType, claimValue) };
    }
}

public class ClaimRequirementFilter : IAuthorizationFilter
{
    readonly Claim _claim;

    public ClaimRequirementFilter(Claim claim)
    {
        _claim = claim;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && c.Value == _claim.Value);
        if (!hasClaim)
        {
            context.Result = new ForbidResult();
        }
    }
}


[Route("api/resource")]
public class MyController : Controller
{
    [ClaimRequirement(MyClaimTypes.Permission, "CanReadResource")]
    [HttpGet]
    public IActionResult GetResource()
    {
        return Ok();
    }
}