具有许多角色的自定义CodeAccessSecurityAttribute

Ale*_*ore 2 .net c# code-access-security security-roles declarative-authorization

我正在为我们的应用程序开发一些基于角色的安全性,并且我本质上是想进行自定义的Verison MVC- AuthorizeAttribute但仅限于业务逻辑层,在该层我们不链接到MVC。

我看过了,PrincipalPermissionAttribute但由于密封,似乎没有办法自定义它。我只想创建一个自定义版本,可以在不使用多个属性的情况下检查任何角色列表中的成员身份,还可以定义在哪里查找角色成员身份。

.Net中缺少类似的内容吗?还是有人在不重新实现ASP.Net的AuthorizeAttribute / RoleProvider / etc的情况下对此有一些见识?

编辑

我目前正在运行命令式版本,但我宁愿使用声明式属性版本,因为在方法/类上方更容易看到它。

现在,我在业务层的抽象基类中具有以下内容:

protected void EnsureEditorLevelAccess()
{
    var allowedRoles = new[]
                            {
                                Roles.Administrator,
                                Roles.Editor,
                            };

    var roles = GetAccountRoles(GetCurrentUsername());

    if (roles.Any(role => allowedRoles.Contains(role)))
    {
        return;
    }

    throw new SecurityException("You do not have sufficient privileges for this operation.");
}
Run Code Online (Sandbox Code Playgroud)

我喜欢使用Roles.Administratoretc,因为角色名称很丑陋(基于Active Directory组...),所以我考虑将这些详细信息包装在自定义属性的构造函数中,我可以将其放到类/方法之上。

GetAccountRoles 只是可注入角色提供程序属性的外观,我可以将其设置为使用AD或使用数据库的测试版本。

我可以继承子类Attribute,但不确定如何启动安全检查。

Nic*_*oiu 5

如果足够满足您的需要,则可以创建一个使用现有PrincipalPermission的新属性。如果您现有的命令式实现使用PrincipalPermission,则应为这种情况。但是,如果命令式版本执行其他操作,则可能需要考虑同时实现自定义权限和相应的属性。如果不确定是否有必要,也许可以分享一些有关当前命令式方法的细节...


问题更新后...

实际上,可以将“ any”逻辑与PrincipalPermission一起使用,尽管它需要合并多个实例,这在属性中使用并不特别实用。这使得创建自定义属性更加合理,该属性可能类似于以下内容:

[Serializable]
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public sealed class AnyRolePermissionAttribute : CodeAccessSecurityAttribute
{
    public AnyRolePermissionAttribute(SecurityAction action)
        : base(action)
    {
    }

    public string Roles { get; set; }

    public override IPermission CreatePermission()
    {
        IList<string> roles = (this.Roles ?? string.Empty).Split(',', ';')
                                .Select(s => s.Trim())
                                .Where(s => s.Length > 0)
                                .Distinct()
                                .ToList();

        IPermission result;
        if (roles.Count == 0)
        {
            result = new PrincipalPermission(null, null, true);
        }
        else
        {
            result = new PrincipalPermission(null, roles[0]);
            for (int i = 1; i < roles.Count; i++)
            {
                result = result.Union(new PrincipalPermission(null, roles[i]));
            }
        }

        return result;
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,您不能在安全属性中使用数组,因此角色列表必须表示为字符串。例如:

[AnyRolePermission(SecurityAction.Demand, Roles = "Foo, Bar")]
Run Code Online (Sandbox Code Playgroud)

您可以通过设计时串联将其与常量一起使用。例如:

[AnyRolePermission(SecurityAction.Demand, Roles = Roles.Administrator + ", " + Roles.Editor)]
Run Code Online (Sandbox Code Playgroud)

对于您的自定义角色提供程序,使用它的适当位置在线程主体中,而不是权限或属性中。例如,如果您当前正在使用GenericPrincipal,则可以将其替换为使用您的自定义角色提供程序来检索目标身份的角色的自定义主体。