Security 基于Asp.net核心代码的策略需要访问“授权”属性
在我的多租户应用程序中,为每个租户设置用户权限(如果您比较熟悉,请阅读角色),因此我们使用值TenantName:Permission向每个用户添加声明 我们使用基于策略的授权,自定义代码使用以下代码Security 基于Asp.net核心代码的策略需要访问“授权”属性,security,asp.net-core,Security,Asp.net Core,在我的多租户应用程序中,为每个租户设置用户权限(如果您比较熟悉,请阅读角色),因此我们使用值TenantName:Permission向每个用户添加声明 我们使用基于策略的授权,自定义代码使用以下代码 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] public class PermissionAuthorizeAttribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public class PermissionAuthorizeAttribute : AuthorizeAttribute
{
public Permission[] AcceptedPermissions { get; set; }
public PermissionAuthorizeAttribute()
{
}
public PermissionAuthorizeAttribute(params Permission[] acceptedPermissions)
{
AcceptedPermissions = acceptedPermissions;
Policy = "RequirePermission";
}
}
public enum Permission
{
Login = 1,
AddUser = 2,
EditOtherUser = 3,
EditBaseData = 6,
EditSettings = 7,
}
通过上面的代码,我们修饰了控制器动作
[PermissionAuthorize(Permission.EditSettings)]
public IActionResult Index()
在startup.cs中,我们有
services.AddAuthorization(options =>
{
options.AddPolicy("RequirePermission", policy => policy.Requirements.Add(new PermissionRequirement()));
});
在这种情况下,AuthorizationHandler需要访问PermissionAuthorization属性,以便我们可以检查在操作上指定了哪些权限。目前,我们可以通过下面的代码获得属性,但我认为肯定有一种更简单的方法,因为在那里有很多迭代
public class PermissionRequirement : AuthorizationHandler<PermissionRequirement>, IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
var filters = ((FilterContext)context.Resource).Filters;
PermissionAuthorizeAttribute permissionRequirement = null;
foreach (var filter in filters)
{
var authorizeFilter = filter as AuthorizeFilter;
if (authorizeFilter == null || authorizeFilter.AuthorizeData == null)
continue;
foreach (var item in authorizeFilter.AuthorizeData)
{
permissionRequirement = item as PermissionAuthorizeAttribute;
if (permissionRequirement != null)
break;
}
if (permissionRequirement != null)
break;
}
//TODO Check that the user has the required claims
context.Succeed(requirement);
return Task.CompletedTask;
}
}
我认为上面的例子会更好,如果你能在控制器/动作中指定年龄,像这样
[Authorize(Policy="OverAge", Age=21)]
public class AlcoholPurchaseRequirementsController : Controller
现在,您需要为每个最低年龄添加不同的政策
关于如何提高效率有什么想法吗?尽管我会使用我的评论,但有一种方法可以实现您的目标:
首先创建一个自定义属性:
public class AgeAuthorizeAttribute : Attribute
{
public int Age{ get; set; }
public AgeAuthorizeAttribute(int age)
{
Age = age;
}
}
然后编写一个过滤器提供程序:
public class CustomFilterProvider : IFilterProvider
{
public int Order
{
get
{
return 0;
}
}
public void OnProvidersExecuted(FilterProviderContext context)
{
}
public void OnProvidersExecuting(FilterProviderContext context)
{
var ctrl = context.ActionContext.ActionDescriptor as ControllerActionDescriptor;
var ageAttr = ctrl.MethodInfo.GetCustomAttribute<AgeAuthorizeAttribute>();
if (ageAttr == null)
{
ageAttr = ctrl.ControllerTypeInfo.GetCustomAttribute<AgeAuthorizeAttribute>();
}
if (ageAttr != null)
{
var policy = new AuthorizationPolicyBuilder()
.AddRequirements(new MinimumAgeRequirement(ageAttr.Age))
.Build();
var filter = new AuthorizeFilter(policy);
context.Results.Add(new FilterItem(new FilterDescriptor(filter, FilterScope.Action), filter));
}
}
}
services.TryAddEnumerable(ServiceDescriptor.Singleton<IFilterProvider, CustomFilterProvider>());
PS:未测试过
是否考虑使用基于资源的授权()?如果采用这种方法,可以将年龄作为资源发送。但是,在这种情况下,您不能使用Authorize
属性。谢谢您的输入。看起来获得实际属性的工作量差不多,所以我想我还是继续使用我的代码吧。
services.TryAddEnumerable(ServiceDescriptor.Singleton<IFilterProvider, CustomFilterProvider>());
[AgeAuthorize(21)]
public IActionResult SomeAction()
... or
[AgeAuthorize(21)]
public class AlcoholPurchaseRequirementsController : Controller