我正在尝试使用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
方法。我尝试了一些解决问题的方法。这些是:
使用新的基于策略的授权(我认为它不适合 我的场景)
使用自定义AuthorizeAttribute
和AuthorizationFilter
(我读过这篇文章
发布https://stackoverflow.com/a/35863514/5426333但我无法正确更改
使用自定义中间件(如何获取当前的AuthorizeAttribute
动作?)
使用ActionFilter(出于安全目的,它是否正确?)
我无法确定哪种方式最适合我的场景以及如何实现它。
第一个问题:MVC5实施不好吗?
第二个问题:您有任何建议实施aspnet核心吗?
答案 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.
}