承担多种角色的自定义 CodeAccessSecurityAttribute

发布于 2024-12-19 08:44:14 字数 1195 浏览 0 评论 0原文

我正在为我们的应用程序开发一些基于角色的安全性,我本质上想要定制 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.Administrator 等,因为角色名称很丑陋(基于 Active Directory 组...),所以我正在考虑将这些细节包装在自定义属性的构造函数中,我可以将其放在类/方法之上。

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

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

I'm working on some Role-based security for our app and I essentially want to do customized verison MVC's AuthorizeAttribute - but only at the business logic layer, where we don't link to MVC.

I've looked at PrincipalPermissionAttribute but it seems it doesn't have a way to customize it as it's sealed. I just want to create a custom version where I can check for membership in any of a list of roles without using multiple attributes, and also define where to look for the role membership.

Is there anything like this in .Net that I'm missing? Or does anybody have some insight on how to do this without reimplementing ASP.Net's AuthorizeAttribute/RoleProvider/etc?

EDIT

I currently have a imperative version running, but I'd rather have a declarative-attribute version, as it's easier to see it above the method/class.

Right now I have the following in an abstract base class for my business layer:

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.");
}

I like being able to use Roles.Administrator etc because the role names are hideous (Active Directory group based...), so I was thinking of wrapping those details up in the constructor of a custom attribute that I can just plop on top of classes/methods.

GetAccountRoles is just a facade over an injectable role-provider property, which I can set to use either AD or a testing version that uses the database.

I could subclass Attribute, but not sure how it would kick off the security check.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

唠甜嗑 2024-12-26 08:44:14

如果足以满足您的需求,您可以创建一个使用现有 PrimaryPermission 的新属性。如果您现有的命令式实现使用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,则可以将其替换为自定义主体,该主体使用自定义角色提供程序来检索目标身份的角色。

You can create a new attribute that uses the existing PrincipalPermission if that would be sufficient for your needs. If your existing imperative implementation uses PrincipalPermission, then this should be the case. However, if your imperative version does something else, you may need to consider implementing both a custom permission and a corresponding attribute. If you're not sure whether this is necessary, perhaps you could share some details regarding your current imperative approach...


After question update...

It's actually possible to use "any" logic with PrincipalPermission, although it requires unioning of multiple instances, which is not particularly practical to work with in an attribute. This makes it much more reasonable to create a custom attribute, which might look something like the following:

[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;
    }
}

Unfortunately, you can't use arrays in security attributes, so the role list has to be represented as a string. e.g.:

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

You could use it with your constants via design-time concatenation. e.g.:

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

As for your custom role provider, the appropriate place to use it is in the thread principal, not the permission or attribute. For example, if you're currently using a GenericPrincipal, you could replace it with a custom principal that uses your custom role provider to retrieve the target identity's roles.

横笛休吹塞上声 2024-12-26 08:44:14

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

本质上,您需要验证allowedRoles.Any(r => Thread.CurrentPrincipal.IsInRole(r))

You could derive your own CodeAccessSecurityAttribute and implement your logic around the Thread.CurrentPrincipal (http://msdn.microsoft.com/en-us/library/system.security.permissions.codeaccesssecurityattribute.aspx).

essentially, you'd want to verify allowedRoles.Any(r => Thread.CurrentPrincipal.IsInRole(r))

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文