C# 为什么ClaimsPrincipalPermissionAttribute是密封的,还有其他选择吗?

C# 为什么ClaimsPrincipalPermissionAttribute是密封的,还有其他选择吗?,c#,wif,custom-attributes,.net-4.5,claims-based-identity,C#,Wif,Custom Attributes,.net 4.5,Claims Based Identity,我正在.NET4.5应用程序中实现基于声明的安全性。要跳过很多障碍,但它基本上是有效的 我唯一不喜欢的是我不能创建自己的属性。ClaimsPrincipalPermissionAttribute已密封。为什么? 在整个申请过程中,我始终在做以下标记: [ClaimsPrincipalPermission(SecurityAction.Demand, Resource = "Foo", Operation = "Bar")] 由于我希望我的资源和操作字符串不会拼写错误,并且易于重构,因此我创建了

我正在.NET4.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继承,但我不能,因为它是密封的:(


是否有其他方法来实现这一点?也许我不需要继承,但我可以注册自己的属性类型,以便它在所有相同的位置都工作吗?

我的直接反应是,这并不需要编写太多内容—您需要多久编写一次?如果它对控制器中的操作通用,请将它放在控制器上-如果适用于多个控制器,请使用该属性创建一个ControllerBase

如果您的情况比这更特殊,我想您必须实现您自己的各种属性

ClaimsPrincipalPermissionAttribute已封存。为什么

谈到了框架内密封类型的共性,由于我们讨论的是代码安全性,这一点非常重要:

每次实现一个方法时,如果该方法采用一个未密封类型的实例,则必须编写该方法,以使其在面对该类型的潜在恶意实例时具有健壮性。您不能依赖于任何已知适用于您的实现的不变量,因为某些恶意网页可能会对您的实现进行子类化,从而覆盖v虚拟方法来做一些会打乱逻辑的事情,并将其传入。每次我密封一个类时,我都可以编写使用该类的方法,并确信我知道该类的功能


在这种情况下,这一点更为重要,
ClaimsPrincipalPermissionAttribute
通过
IClaimsPrincipal
接口进行检查。因此,通过将
ClaimsPrincipalPermissionAttribute
密封,它们允许
IClaimsPrincipal
的任何实现者不必担心恶意实现。这是一个相当大的挑战节省,因为这都与安全相关。

ClaimsPrincipalPermissionAttribute
源于
CodeAccessSecurityAttribute
。它除了实现
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
,如下所述


另外,请注意构造函数参数的默认值的使用。您需要一个构造函数,该构造函数接受属性的
SecurityAction
参数,以便由框架实例化。在这种情况下,
DemandPermission
可能是一个坏名字,因为您可以重写
SecurityAction
除了
SecurityAction.Demand

之外,几乎每个操作都有一个单独的必需权限集,因此需要经常编写。我想这只是我们必须面对的问题。感谢您提供详细的答案。从这个角度来看,这很有意义。不过,我还是希望有一些内置的重载这个清洁器-但我想它们必须在框架内完成,以避免恶意实现。有趣的是,我不知道为什么我以前没有想到。你知道框架是否会像
ClaimsPrincipalPermissionAttribute
那样接受它吗?我想这取决于它是否专门寻找或者,如果它正在寻找
CodeAccessSecurityAttribute
的任何派生成员,我将不得不进行实验并查看!它确实被提取了。事实上,正是ClaimsPrincipPermission在ClaimsAuthorizationManager中挂钩并在其Demand()内调用CheckAccess实现。添加了一个额外的注意事项-我忘了提到自定义属性必须在单独的程序集中定义。另一个更新-您需要在属性构造函数中定义一个默认值,以便像您所希望的那样使用它。感谢您提供的详细信息。我还需要一段时间才能验证此方法在我们的应用程序中是否有效,但我当我有机会尝试时,我会通知你的。再次感谢!