【问题标题】:Why is ClaimsPrincipalPermissionAttribute sealed, and is there an alternative?为什么 ClaimsPrincipalPermissionAttribute 是密封的,还有替代方案吗?
【发布时间】:2012-08-18 07:31:20
【问题描述】:

我正在我的 .net 4.5 应用程序中实现基于声明的安全性。有很多障碍要跳过,但它基本上是有效的。

我唯一不喜欢的部分是我无法创建自己的属性。 ClaimsPrincipalPermissionAttribute 是密封的。为什么?

我总是在我的申请中做标记,例如:

[ClaimsPrincipalPermission(SecurityAction.Demand, Resource = "Foo", Operation = "Bar")]

而且由于我希望我的资源和操作字符串不会拼写错误并且易于重构,因此我创建了类,因此我可以这样做:

[ClaimsPrincipalPermission(SecurityAction.Demand, Resource = Resources.Foo, Operation = Operations.Foo.Bar)]

(请注意,由于不同的资源可能有不同的操作,操作本身是由资源子类化的。)

这一切都很好,很花哨,但每次都需要输入或复制/粘贴。我宁愿做这样的事情:

[DemandPermission(Resources.Foo, Operations.Foo.Bar)]

我可以创建这个属性,但我需要从 ClaimsPrincipalPermissionAttribute 继承,因为它是密封的,所以我不能。 :(

还有其他方法可以解决这个问题吗?也许我不需要继承,但我可以注册我自己的属性类型以便它在所有相同的地方工作吗?

【问题讨论】:

    标签: c# wif custom-attributes .net-4.5 claims-based-identity


    【解决方案1】:

    我的直接反应是,这不是很多要写的 - 你需要多久写一次?如果它适用于控制器中的操作,请将其放在控制器上 - 如果适用于许多控制器,请创建具有该属性的 ControllerBase。

    如果你的情况比这更特殊,我想你不得不实现你自己的那个属性。

    【讨论】:

    • 几乎每个操作都有单独的所需权限集,因此需要经常编写。我想这只是我们必须忍受的事情。
    【解决方案2】:

    ClaimsPrincipalPermissionAttribute 是密封的。为什么?

    Eric Lippert讲了Framework类型中sealed的共性,既然我们在讲代码安全,这点就很重要了:

    每次您实现一个采用非密封类型实例的方法时,您必须编写该方法以使其在面对该类型的潜在敌对实例时保持稳健。你不能依赖任何你知道对你的实现是正确的不变量,因为一些恶意网页可能会继承你的实现,覆盖虚拟方法来做一些弄乱你的逻辑的事情,然后把它传递进去。每次我密封一个类,我可以编写使用该类的方法,并且确信我知道该类的作用。

    这在这种情况下更为重要,ClaimsPrincipalPermissionAttribute 通过IClaimsPrincipal 接口进行检查。因此,通过使ClaimsPrincipalPermissionAttribute 密封,它们允许IClaimsPrincipal 的任何实现者不必担心恶意实现。考虑到这一切都与安全相关,这是一笔可观的节省。

    【讨论】:

    • 感谢您的详细解答。从这个角度来看,这很有意义。尽管如此,我还是希望有一些内置的重载来使这个更干净——但我想它们必须在框架中完成以避免恶意实现。
    【解决方案3】:

    ClaimsPrincipalPermissionAttribute 派生自 CodeAccessSecurityAttribute。除了根据您传入的 Resource 和 Operation 的值实现 CreatePermission() 返回一个新的 ClaimsPrincipalPermission 之外,它几乎什么都不做。

    您可以实现一个派生自 CodeAccessSecurityAttribute 的新类(这不是密封的),它可以满足您的需求。

    使用JustDecompile可以看到ClaimsPrincipalPermissionAttribute中的代码很简单。您可以像这样创建自己的属性:

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)]
    public sealed class DemandPermissionAttribute : CodeAccessSecurityAttribute
    {
        public Operations Operation { get; set; }
        public Resources Resource { get; set; }
    
        public DemandPermissionAttribute(SecurityAction action = SecurityAction.Demand)
            : base(action)
        {
        }
    
        public override IPermission CreatePermission()
        {
            return new ClaimsPrincipalPermission(this.Resource.ToString(), this.Operation.ToString());
        }
    }
    

    对此需要注意的重要一点是,您必须在与引用它的程序集不同的程序集中定义自定义属性,否则框架将抛出​​TypeLoadException,如此处所述

    http://msdn.microsoft.com/en-us/library/vstudio/yaah0wb2.aspx

    另外,请注意构造函数参数的默认值的使用。您需要有一个构造函数,该构造函数采用 SecurityAction 参数,以便框架实例化该属性。在这种情况下,DemandPermission 可能是一个坏名称,因为您可以将 SecurityAction 覆盖为 SecurityAction.Demand 以外的其他名称。

    【讨论】:

    • 有趣。我不知道为什么我以前没有想到这一点。你知道这是否会像ClaimsPrincipalPermissionAttribute 一样被框架接收到?我想这取决于它是专门寻找它,还是寻找CodeAccessSecurityAttribute 的任何派生成员。我得试验一下看看!
    • 它确实被捡起来了。事实上,它是 ClaimsPrincipalPermission 挂钩 ClaimsAuthorizationManager 并在其 Demand() 实现中调用 CheckAccess。
    • 添加了一个额外的注释 - 我忘了提到自定义属性必须在单独的程序集中定义。
    • 还有另一个更新 - 您需要在属性构造函数中定义一个默认值,以便按照您的意愿使用它。
    • 感谢您提供详细信息。我需要一段时间才能验证这种方法是否适用于我们的应用程序,但当我有机会尝试时,我会更新你。再次感谢!
    猜你喜欢
    • 1970-01-01
    • 2019-06-16
    • 1970-01-01
    • 2011-02-23
    • 2019-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多