使用ActionFilterAttribute

时间:2016-12-14 14:39:17

标签: asp.net asp.net-mvc asp.net-core authorization

我正在使用一种方法(我认为是一种黑客行为)来传递值,以使特定于操作的权限数据可用于自定义授权策略。

问题? 我需要一种方法将特定于操作的数据传递给授权需求处理程序。

不希望在启动

中执行此操作
services.AddAuthorization(options =>
        {               
            options.AddPolicy("MyPolicy",policy =>policy.Requirements.Add(new PermissionRequirement(PermissionEnum.CanView)));
        });

这在控制器中

[Authorize(Policy = "MyPolicy")]
public IActionResult MyAction()
    {          
        return View();
    }

因为我需要很多权限,所以要求我有太多的政策。所以我需要能够将PermissionEnum.CanView传递给操作级别的授权处理程序,以便我可以指定每个操作所需的权限,并让相同的处理程序处理它们。

我无法找到一种方法将值传递给策略属性,所以我这样做了。我创建了一个自定义ActionFilterAttribute并传递了值

public class AccessAttribute : ActionFilterAttribute
{
    public string Permission { get; }

    public AccessAttribute(PermissionEnum permission)
    {
        Permission =  permission.ToString();
    }
}

然后我像这样使用它

[Access(PermissionEnum.CanView)]
[Authorize(Policy = "MyPolicy")]
public IActionResult MyAction()
{          
    return View();
}

然后在授权处理程序中,我从AuthorizationHandlerContext对象中访问此值,如此

var mvcContext = context.Resource as AuthorizationFilterContext;
var descriptor = mvcContext?.ActionDescriptor as ControllerActionDescriptor;

if (descriptor == null) return Task.CompletedTask;

foreach (var filterMetadata in mvcContext.Filters)
{
if (filterMetadata.GetType().Name != typeof(AccessAttribute).Name) continue;

var PermissionAttribute = filterMetadata as AccessAttribute;

if (PermissionAttribute == null) continue;
if (context.User.HasClaim(c => c.Type == PermissionAttribute.Permission))
{
context.Succeed(requirement);
}
}   

return Task.CompletedTask;

在这里,我以声明的形式向用户提供了上下文权限,因此我检查用户是否具有该操作的指示权限。最重要的是,我可以通过在我的自定义过滤器中将权限设置为属性来判断哪些用户可以访问操作。

一切正常但我想知道这是否可行。我假设可能有更好的方法来执行此操作,甚至可以将值传递给自定义策略。

问题:

1)有更好的方法吗?

2)我从调试中注意到我的每个自定义过滤器在开始时运行两次。这可能不是问题,但我只是想知道为什么twice。他们不会在任何时候访问一个动作(这对我很好),因为它是一个硬代码,无论动作执行多少次都不会改变。我只是想知道这些,但我的第一个问题是我的实际关注。

2 个答案:

答案 0 :(得分:0)

[编辑] 基于“传统”ASP.NET MVC的答案,可轻松适应ASP.NET Core MVC。

  

所以我需要能够在操作级别将PermissionEnum.CanView传递给授权处理程序,以便我可以指定每个操作所需的权限,并让相同的处理程序处理它们。

一个选项是创建一个自定义AuthorizeAttribute,在其构造函数中接收权限:

public class CustomAuthorization : AuthorizeAttribute, IAuthorizationFilter
{
    private readonly string _permission = "";

    // use your Enum instead of string        
    public CustomAuthorization(string permission) : base()
    {
        _permission = permission;
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (_permission == "xxx" && Roles.Contains("yyyy"))
        {
            // do something interesting
            return true;
        }
        return false;
    }
}

及其用法:

    [CustomAuthorization("My Permission", Roles = "MyRoles")]
    public ActionResult About()
    {
        ViewBag.Message = "Your application description page.";

        return View();
    }

答案 1 :(得分:0)

Microsoft认可的用于完成所需操作的技术是实现自定义授权策略提供程序。基本思想是,对于每种可能的方案(例如MyPolicy_CanView等),您将拥有一个单独的策略名称,但是您不会针对每个方案进行注册;您的自定义授权策略提供程序将在每次调用带有authorize属性的操作方法时被调用,并且它将看到策略名称与给定模式匹配(例如,在此示例中以MyPolicy_开头),然后使用您的一个需求以及它从名称中提取的参数(例如,本示例中的CanView)。如果您不希望在控制器代码中显示自定义名称(MyPolicy_CanView),也可以使用自定义属性来构建策略名称。

以下是Microsoft文档,其中包含详细信息和示例代码:https://docs.microsoft.com/en-us/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-3.1

Hackernoon描述了该技术,但通过从DefaultAuthorizationPolicyProvider派生而不是实现IAuthorizationPolicyProvider来实现自定义策略提供者:https://hackernoon.com/permission-based-authorization-asp-net-core-with-authorizationpolicyprovider-af4933d575ee