为什么ClaimsPrincipalPermissionAttribute被封存了,是否有替代品?

4

我正在我的.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,但我不能因为它被密封了。 :(

有其他方法来解决这个问题吗?也许我不需要继承,但我能注册自己的属性类型以使其在所有相同的地方起作用吗?

3个回答

7
ClaimsPrincipalPermissionAttribute继承自CodeAccessSecurityAttribute。它几乎什么也不做,除了实现CreatePermission(),根据您传递的Resource和Operation的值返回一个新的基于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的成员。我需要进行实验并观察! - Matt Johnson-Pint
它确实被捕获了。事实上,正是ClaimsPrincipalPermission钩入了ClaimsAuthorizationManager并在其Demand()实现中调用CheckAccess。 - Mike Goodwin
添加了一条额外的注释 - 我忘记提到自定义属性必须在单独的程序集中定义。 - Mike Goodwin
另外一个更新 - 你需要在属性构造函数中定义一个默认值,才能像你想要的那样使用它。 - Mike Goodwin
感谢提供详细信息。在我们的应用程序中验证这种方法是否可行需要一些时间,但是我会在有机会尝试时及时更新您。再次感谢! - Matt Johnson-Pint
这种方法可行并且应该被接受作为答案。然而,我不必在一个单独的程序集中定义自定义属性,而是将所有代码放在一个大程序集中就可以了。 - user20358

3
ClaimsPrincipalPermissionAttribute是sealed的。为什么?
Eric Lippert提到了Framework类型中sealed的普遍性,而且由于我们谈论的是代码安全性,这一点非常重要:
每次你实现一个接受未密封类型实例的方法时,你必须编写该方法以应对可能敌对的该类型实例。你不能依赖于你知道的关于你的实现真实的任何不变量,因为某些敌对的网页可能会子类化你的实现,覆盖虚拟方法来执行破坏你逻辑的操作,然后传递它。每次我密封一个类,我可以编写使用该类的方法,并确信我知道该类的作用。
在这种情况下,这更加重要,ClaimsPrincipalPermissionAttribute通过IClaimsPrincipal接口进行检查。因此,通过使ClaimsPrincipalPermissionAttribute密封,他们允许IClaimsPrincipal的任何实现者不必担心敌对实现。考虑到这都与安全有关,这是一项相当节省的措施。

感谢您提供详细的答案。从那个角度来看,这很有道理。不过,我希望有一些内置的重载来使代码更加简洁 - 但我猜它们必须在框架中完成,以避免敌对实现。 - Matt Johnson-Pint

1

我的第一反应是这不是很需要写的东西,你有多频繁需要写它呢?如果它是控制器中通用的操作,请将其放在控制器上;如果适用于许多控制器,请创建一个带有该属性的 ControllerBase。

如果您的情况比这更特殊,我想您可能被迫实现自己的那种属性。


1
几乎每个操作都有单独的所需权限设置,因此需要经常编写。我想这只是我们必须接受的事情。 - Matt Johnson-Pint

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接