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

ade*_*lin 38 c# asp.net-mvc access-control asp.net-core

我正在尝试使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

[HttpGet]
[CustomAuthorize(PermissionEnum.PERSON_LIST)]
public ActionResult Index(PersonListQuery query){ }
Run Code Online (Sandbox Code Playgroud)

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

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

}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 使用自定义AuthorizeAttributeAuthorizationFilter(我读这篇文章 /sf/answers/2510446011/但我无法正确更改)

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

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

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

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

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

Tse*_*eng 55

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

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并在您的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>();
Run Code Online (Sandbox Code Playgroud)

最后在控制器中

[HttpGet]
[Authorize(Policy = PermissionEnum.PERSON_LIST.ToString())]
public ActionResult Index(PersonListQuery query)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

该解决方案的好处是,你也可以有一个要求,即多个处理程序,如果第一个成功的第二处理器能够确定这是一个失败,你可以使用它以资源为基础的授权费多大力量.

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

来自blowdart:

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

  • 使用asp.net core rc1我无法使用[Authorize(Policy = PermissionEnum.PERSON_LIST.ToString())]因为编译器只需要在Attribute声明中使用"常量".turn-arround是使用如下属性:[Authorize(Policy = nameof(PermissionEnum.PERSON_LIST))] (7认同)