2

我正在为我们的应用程序开发一些基于角色的安全性,我基本上想做定制的版本 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.");
}

我喜欢能够使用Roles.Administratoretc,因为角色名称是可怕的(基于 Active Directory 组......),所以我正在考虑将这些细节包装在自定义属性的构造函数中,我可以在类/方法之上扑通一声.

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

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

4

2 回答 2

5

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


问题更新后...

实际上可以将“任何”逻辑与 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;
    }
}

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

[AnyRolePermission(SecurityAction.Demand, Roles = "Foo, Bar")]

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

[AnyRolePermission(SecurityAction.Demand, Roles = Roles.Administrator + ", " + Roles.Editor)]

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

于 2011-12-02T15:35:11.153 回答
0

您可以派生自己的 CodeAccessSecurityAttribute 并围绕 Thread.CurrentPrincipal (http://msdn.microsoft.com/en-us/library/system.security.permissions.codeaccesssecurityattribute.aspx) 实现您的逻辑。

本质上,你想验证allowedRoles.Any(r => Thread.CurrentPrincipal.IsInRole(r))

于 2011-12-02T15:44:27.123 回答