tabindex="0" keywords="frlrfSystemSecurityCodeAccessPermissionClassAssertTopic"> Assert方法
tabindex="0" keywords="frlrfSystemSecurityCodeAccessPermissionClassAssertTopic"> msdn
tabindex="0" keywords="frlrfSystemSecurityCodeAccessPermissionClassAssertTopic">Assert 是一种可以对代码访问权限类和 tabindex="0" keywords="frlrfSystemSecurityPermissionSetClassTopic">PermissionSet 类调用的方法。您可以使用 Assert 来允许您的代码以及下游调用方执行某些有特殊权限要求的操作,这种操作指:您的代码有权限执行、但代码的调用方可能没有权限执行的操作。安全断言可更改运行库在安全检查期间执行的正常过程。当您断言一个权限时,即通知安全系统不要检查您的代码的调用方是否有断言的权限。
警告 请谨慎使用断言,因为它们会打开安全漏洞,并会破坏运行库实施安全限制的机制。
在库调用非托管代码或进行需要某权限的调用而该权限与库的本来用途不明显相关时,断言很有用。例如,调用非托管代码的所有托管代码必须具有指定了 UnmanagedCode 标志的 SecurityPermission。默认情况下,将不向不是从本地计算机产生的代码(例如从本地 Intranet 下载的代码)授予此权限。因此,为了使从本地 Internet 下载的代码能够调用使用非托管代码的库,它必须让库断言该权限。另外,某些库可能会进行调用方看不到而又需要特殊权限的调用。
还可以在您的代码以一种完全对调用方隐藏的方式访问资源时使用断言。例如,假定您的库从数据库获取信息,并且在此过程中还从计算机注册表读取信息。因为使用您的库的开发人员无权访问您的源代码,所以他们无从知道他们的代码需要 RegistryPermission 才能使用您的代码。在这种情况下,如果您确定要求您的代码的调用方拥有访问注册表的权限不合理或不必要,则可以断言用来读取注册表的权限。在这种情况下,由该库断言权限就是恰当的,这样可以使没有 RegistryPermission 的调用方能够使用该库。
只有在断言的权限和下游调用方要求的权限属于同一类型并且要求的权限是断言的权限的子集时,断言才会影响堆栈步。例如,如果您断言用来读取 C 驱动器上的所有文件的 FileIOPermission,同时发出了一个下游要求,要求读取 C:/Temp 中的文件的 FileIOPermission,则断言会影响堆栈步;但是,如果要求写入 C 驱动器的 FileIOPermission,则断言无效。
若要执行断言,您的代码必须同时被授予您要断言的权限和表示执行断言的权限的 tabindex="0" keywords="frlrfsystemsecuritypermissionssecuritypermissionclasstopic">SecurityPermission。虽然您可以断言尚未授予您的代码的权限,但此断言是无效的,因为在您的断言还没有能够使安全检查成功之前,安全检查就会失败。
下图说明在使用 Assert 时会发生什么情况。假定下面有关程序集 A、B、C、E 和 F 以及两个权限 P1 和 P1a 的语句为真:
- P1a 表示读取 C 驱动器上 .txt 文件的权限。
- P1 表示读取 C 驱动器上所有文件的权限。
- P1a 和 P1 都是 FileIOPermission,P1a 是 P1 的子集。
- 已将 P1a 权限授予程序集 E 和 F。
- 已将 P1 权限授予程序集 C。
- P1 和 P1a 两个权限均未授予程序集 A 和 B。
- 方法 A 包含在程序集 A 中,方法 B 包含在程序集 B 中,依此类推。
使用 Assert
在此方案中,方法 A 调用 B,B 调用 C,C 调用 E,E 调用 F。方法 C 断言读取 C 驱动器上的文件的权限(权限 P1),方法 E 请求读取 C 驱动器上的 .txt 文件的权限(权限 P1a)。在运行时遇到 F 中的要求时,就会执行堆栈步来从 E 开始检查 F 的所有调用方的权限。E 已被授予 P1a 权限,所以堆栈步会接着检查 C 的权限,然后会在其中发现 C 的断言。因为请求的权限 (P1a) 是断言的权限 (P1) 的子集,所以堆栈步会停止,安全检查自动成功。没有将权限 P1a 授予程序集 A 和 B 并不重要。通过断言 P1,即使没有授予调用方访问 P1 所保护资源的权限,方法 C 也可确保其调用方能够访问该资源。
如果设计类库,且有一个类访问受保护的资源,则在多数情况下应该安全要求(要求该类的调用方拥有适当的权限)。如果该类随后会执行一个操作,您知道此操作的多数调用方没有相应的权限,并且您愿承担责任让这些调用方调用您的代码,则可以通过对一个权限对象(该对象表示代码所执行的操作)调用 Assert 方法来断言该权限。以此方式使用 Assert 可让通常不能调用您的代码的调用方来调用您的代码。因此,如果断言权限,则务必提前执行适当的安全检查以防止您的组件被滥用。
例如,假定您的高度信任的库类有一个删除文件的方法。它通过调用非托管 Win32 函数来访问文件。调用方调用您的代码的 Delete 方法,传入要删除的文件的名称 C:/Test.txt。在 Delete 方法内,您的代码创建一个 tabindex="0" keywords="frlrfSystemSecurityPermissionsFileIOPermissionClassTopic">FileIOPermission 对象,该对象表示对 C:/Test.txt 的写访问权。(删除文件需要写访问权。)您的代码然后通过调用 FileIOPermission 对象的 Demand 方法来调用强制安全检查。如果调用堆栈中的一个调用方没有此权限,则会引发 tabindex="0" keywords="frlrfSystemSecuritySecurityExceptionClassTopic">SecurityException。如果没有引发异常,则可判定所有调用方都有权访问 C:/Test.txt。因为您相信多数调用方将没有访问非托管代码的权限,所以您的代码创建一个 tabindex="0" keywords="frlrfSystemSecurityPermissionsSecurityPermissionClassTopic">SecurityPermission 对象(该对象表示调用非托管代码的权限),并调用对象的 Assert 方法。最后,它调用非托管的 Win32 函数来删除 C:/Text.txt 并将控制焦点返回给调用方。
警告 您必须确保您的代码不在其他代码可以使用您的代码来访问所断言权限保护的资源时使用断言。例如,在写入一个文件(该文件的名称由调用方以参数形式指定)的代码中,您不能断言用于写入文件的 FileIOPermission,否则,您的代码会任凭第三方滥用。
如果在同一方法中对多个权限调用 Assert 方法,则可能出现意外行为。相反,您应当创建一个 PermissionSet 对象,向它传递您要调用的各个权限,然后对该 PermissionSet 对象调用 Assert 方法。
下面的示例说明使用 Assert 方法重写安全检查的声明式语法。注意,FileIOPermissionAttribute 语法采用了两个值:一个 tabindex="0" keywords="frlrfSystemSecurityPermissionsSecurityActionClassTopic">SecurityAction 枚举和要授予其权限的文件或目录的位置。即使不检查调用方是否有权限访问 C:/Log.txt
,调用 Assert 也可使访问该文件的要求成功。
[Visual Basic]
Option Explicit
Option Strict
Imports System
Imports System.IO
Imports System.Security.Permissions
Namespace LogUtil
Public Class Log
Public Sub New()
End Sub
<FileIOPermission(SecurityAction.Assert, All := "C:/Log.txt")> Public Sub
MakeLog()
Dim TextStream As New StreamWriter("C:/Log.txt")
TextStream.WriteLine("This Log was created on {0}", DateTime.Now) '
TextStream.Close()
End Sub
End Class
End Namespace
[C#]
namespace LogUtil
{
using System;
using System.IO;
using System.Security.Permissions;
public class Log
{
public Log()
{
}
[FileIOPermission(SecurityAction.Assert, All = @"C:/Log.txt")]
public void MakeLog()
{
StreamWriter TextStream = new StreamWriter(@"C:/Log.txt");
TextStream.WriteLine("This Log was created on {0}", DateTime.Now);
TextStream.Close();
}
}
}
下面的代码段说明使用 Assert 方法重写安全检查的强制性语法。在此示例中,声明了 FileIOPermission 对象的一个实例。向其构造函数传递 FileIOPermissionAccess.Read 来定义允许的访问类型,后面是描述文件位置的字符串。定义 FileIOPermission 对象后,您只需要调用其 Assert 方法即可重写安全检查。
[Visual Basic]
Option Explicit
Option Strict
Imports System
Imports System.IO
Imports System.Security.Permissions
Namespace LogUtil
Public Class Log
Public Sub New()
End Sub 'New
Public Sub MakeLog()
Dim FilePermission As New FileIOPermission(FileIOPermissionAccess.AllAccess, "C:/Log.txt")
FilePermission.Assert()
Dim TextStream As New StreamWriter("C:/Log.txt")
TextStream.WriteLine("This Log was created on {0}", DateTime.Now)
TextStream.Close()
End Sub
End Class
End Namespace
[C#]
namespace LogUtil
{
using System;
using System.IO;
using System.Security.Permissions;
public class Log
{
public Log()
{
}
public void MakeLog()
{
FileIOPermission FilePermission = new FileIOPermission(FileIOPermissionAccess.AllAccess,@"C:/Log.txt");
FilePermission.Assert();
StreamWriter TextStream = new StreamWriter(@"C:/Log.txt");
TextStream.WriteLine("This Log was created on {0}", DateTime.Now);
TextStream.Close();
}
}
}