如何使用Asp.Net Core实现基于权限的访问控制

时间:2016-04-06 08:38:35

标签: c# asp.net-mvc asp.net-core access-control

我正在尝试使用aspnet核心实现基于权限的访问控制。为了动态管理用户角色和权限(create_product,delete_product等),它们存储在数据库中。数据模型类似于http://i.stack.imgur.com/CHMPE.png

在aspnet核心之前(在MVC 5中)我使用下面的自定义AuthorizeAttribute来处理这个问题:

public class CustomAuthorizeAttribute : AuthorizeAttribute
{
    private readonly string _permissionName { get; set; }
    [Inject]
    public IAccessControlService _accessControlService { get; set; }

    public CustomAuthorizeAttribute(string permissionName = "")
    {
        _permissionName = permissionName;
    }

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
        var user = _accessControlService.GetUser();
        if (PermissionName != "" && !user.HasPermission(_permissionName))
        {
            // set error result
            filterContext.HttpContext.Response.StatusCode = 403;
            return;
        }
        filterContext.HttpContext.Items["CUSTOM_USER"] = user;
    }
}

然后我在下面的动作方法中使用它:

[HttpGet]
[CustomAuthorize(PermissionEnum.PERSON_LIST)]
public ActionResult Index(PersonListQuery query){ }

此外,我在视图中使用HttpContext.Items [" CUSTOM_USER"]来显示或隐藏html部分:

@if (CurrentUser.HasPermission("<Permission Name>"))
{

}

当我决定切换aspnet核心时,我的所有计划都失败了。因为OnAuthorization中没有虚拟AuthorizeAttribute方法。我尝试了一些解决问题的方法。这些是:

  • 使用新的基于策略的授权(我认为它不适合 我的场景)

  • 使用自定义AuthorizeAttributeAuthorizationFilter(我读过这篇文章 发布https://stackoverflow.com/a/35863514/5426333但我无法正确更改

  • 使用自定义中间件(如何获取当前的AuthorizeAttribute 动作?)

  • 使用ActionFilter(出于安全目的,它是否正确?)

我无法确定哪种方式最适合我的场景以及如何实现它。

第一个问题:MVC5实施不好吗?

第二个问题:您有任何建议实施aspnet核心吗?

3 个答案:

答案 0 :(得分:49)

根据评论,这里有一个关于如何使用基于策略的授权的示例:

public class PermissionRequirement : IAuthorizationRequirement
{
    public PermissionRequirement(PermissionEnum permission)
    {
         Permission = permission;
    }

    public PermissionEnum Permission { get; }
}

public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
{
    private readonly IUserPermissionsRepository permissionRepository;

    public PermissionHandler(IUserPermissionsRepository permissionRepository)
    {
        if(permissionRepository == null)
            throw new ArgumentNullException(nameof(permissionRepository));

        this.permissionRepository = permissionRepository;
    }

    protected override void Handle(AuthorizationContext context, PermissionRequirement requirement)
    {
        if(context.User == null)
        {
            // no user authorizedd. Alternatively call context.Fail() to ensure a failure 
            // as another handler for this requirement may succeed
            return null;
        }

        bool hasPermission = permissionRepository.CheckPermissionForUser(context.User, requirement.Permission);
        if (hasPermission)
        {
            context.Succeed(requirement);
        }
    }
}

并在Startup课程中注册:

services.AddAuthorization(options =>
{
    UserDbContext context = ...;
    foreach(var permission in context.Permissions) 
    {
        // assuming .Permission is enum
        options.AddPolicy(permission.Permission.ToString(),
            policy => policy.Requirements.Add(new PermissionRequirement(permission.Permission)));
    }
});

// Register it as scope, because it uses Repository that probably uses dbcontext
services.AddScope<IAuthorizationHandler, PermissionHandler>();

最后在控制器中

[HttpGet]
[Authorize(Policy = PermissionEnum.PERSON_LIST.ToString())]
public ActionResult Index(PersonListQuery query)
{
    ...
}

此解决方案的优势在于您还可以为需求提供多个处理程序,例如,如果第一个处理程序成功,则第二个处理程序可以确定它失败并且您可以将resource based authorization用于少量额外处理程序努力。

基于策略的方法是ASP.NET核心团队执行此操作的首选方法。

来自blowdart

  

我们不希望您编写自定义授权属性。如果你需要这样做我们做错了什么。相反,你应该写授权要求。

答案 1 :(得分:1)

对于不需要为每个权限添加策略的解决方案,请参阅我的回答以获取其他问题。

&#xA;&#xA;

它允许您使用您希望的任何自定义属性来装饰控制器和操作,并在AuthorizationHandler中访问它们。

&#xA;

答案 2 :(得分:1)

我有相同的要求,并且已按照以下说明进行操作,对我来说很好。我正在使用.Net Core 2.0 Webapi

         [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class CheckAccessAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    private string[] _permission;
    public CheckAccessAttribute(params string[] permission)
    {
        _permission = permission;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = context.HttpContext.User;

        if (!user.Identity.IsAuthenticated)
        {
            return;
        }

        IRepository service = (IRepositoryWrapper)context.HttpContext.RequestServices.GetService(typeof(IRepository));
          var success = service.CheckAccess(userName, _permission.ToList());
            if (!success)
            {
                context.Result = JsonFormatter.GetErrorJsonObject(CommonResource.error_unauthorized,
                        StatusCodeEnum.Forbidden);
                return;
            }
        return;
    }
}

在Controller中按如下所示使用它

        [HttpPost]
    [CheckAccess(Permission.CreateGroup)]
    public JsonResult POST([FromBody]Group group)
    {
       // your code api code here.
    }