作者:Angela Crocker、Andy Olsen和Edward Jezierski
适用于:
Microsoft? .NET 应用程序
摘要:
学习向 Microsoft .NET 应用程序公开数据的最佳方式,以及如何实现一个有效的策略以便在分布式应用程序的层间传递数据。(本文包含一些指向英文站点的链接。)
目录
- 简介
- 将关系数据映射到业务实体
- 实现数据访问逻辑组件
- 实现业务实体
- 事务处理
- 验证
- 异常管理
- 授权与安全性
- 部署
- 附录及其合作者
授权与安全性
学习向 Microsoft .NET 应用程序公开数据的最佳方式,以及如何实现一个有效的策略以便在分布式应用程序的层间传递数据。(本文包含一些指向英文站点的链接。)
本节说明如何将安全性应用于数据访问逻辑组件和业务实体组件。.NET 公共语言运行库使用权限对象实现其对托管代码的强制限制机制。权限对象有三种,各自具有特定的用途:
代码访问安全性。这些权限对象用于防止未经授权使用资源和操作。
身份标识。这些权限对象指定运行程序集时所必需的身份标识特征。
基于角色的安全性。这些权限对象提供了一个机制,用于判断用户(或用户的代理人)是否具有特定身份标识,或者是否是指定角色的成员。PrincipalPermission 对象是唯一基于角色的安全性权限对象。
托管代码可以使用 Principal 对象(包含对 Identity 对象的引用)来判断当事人的身份标识或角色。把 Identity 对象和 Principal 对象与用户、组帐户等大家所熟悉的概念比较可能会更容易理解。在 .NET Framework 中,Identity 对象表示用户,而角色表示成员身份和安全性环境。Principal 对象封装了 Identity 对象和角色。.NET Framework 中的应用程序根据 Principal 对象的身份标识或角色成员身份(后者更常见)授予 Principal 对象权限。
数据访问逻辑组件中的安全性建议
数据访问逻辑组件的设计目的是供其他应用程序组件使用,它也是您的应用程序代码中在调用程序可以访问数据之前实现安全性的最后一个地方。
通常,数据访问逻辑组件可以依赖于由调用程序设置的安全性环境。然而,有些情况下数据访问逻辑组件必须执行自己的授权检查,以确定是否允许当事人执行所请求的操作。授权在身份验证后进行,并使用当事人身份标识与角色等有关信息来确定该当事人可以访问的资源。
在以下情况下,应在数据访问逻辑组件层次上执行授权检查:
需要与不完全信任的业务过程开发人员共享数据访问逻辑组件
需要保护对数据存储提供的强大功能的访问
在定义了 Identity 对象和 Principal 对象后,可以用三种方式执行基于角色的安全性检查:
使用 PrincipalPermission 对象执行强制性安全性检查。
使用 PrincipalPermissionAttribute 属性执行说明性安全性检查。
使用 Principal 对象中的属性和 IsInRole 方法执行显式安全性检查。
以下代码示例显示了如何使用 PrincipalPermissionAttribute 为数据访问逻辑组件类中的方法指定基于角色的声明性安全性检查:
using System;
using System.Security.Permissions;
public class CustomerDALC
{
public CustomerDALC()
{
}
// 使用 PrincipalPermissionAttribute 要求此方法的调用程序
// 具有一个名为“MyUser”的身份标识,并且属于角色“Administrator”。
[PrincipalPermissionAttribute(SecurityAction.Demand,
Name="MyUser", Role="Administrator")]
public void DeleteCustomer(string customerID)
{
// 在此处删除客户代码
}
}
|
以下代码显示了如何创建具有所需身份标识和角色的 Principal 对象,以便对 CustomerDALC 对象调用 DeleteCustomer 方法:
using System;
using System.Security.Principal;
using System.Threading;
public class MainClass
{
public static int Main(string[] args)
{
Console.Write("用户名:");
string UserName = Console.ReadLine();
Console.Write("密码:");
string Password = Console.ReadLine();
if (Password == "password" && UserName == "MyUser")
{
// 创建一个名为“MyUser”的通用身份标识
GenericIdentity MyIdentity = new GenericIdentity("MyUser");
// 创建角色
String[] MyString = {"Administrator", "User"};
// 创建一个通用当事人
GenericPrincipal MyPrincipal = new GenericPrincipal(MyIdentity,
MyString);
// 设置此线程的当前当事人,以用于基于角色的安全性
Thread.CurrentPrincipal = MyPrincipal;
}
// 创建一个 CustomerDALC 对象,并尝试调用它的 DeleteCustomer 方法。
// 仅在当前当事人的身份标识和角色合格时这一步骤才能成功。
CustomerDALC c = new CustomerDALC();
c.DeleteCustomer("VINET");
}
}
|
Windows 身份验证
理想情况下,在连接到数据库时应使用 Windows 身份验证而不是 SQL Server 身份验证。然而,应使用服务帐户并避免模拟连接到数据库,因为它会妨碍连接池。连接池需要相同的连接字符串;如果尝试使用不同的连接字符串打开数据库,就会创建单独的连接池,而这将限制可缩放性。
有关 Windows 身份验证和连接池的详细信息,
安全通信建议
要实现调用应用程序与数据访问逻辑组件之间的安全通信,请考虑以下建议:
如果数据访问逻辑组件是通过各种层的线路调用的,并且信息交换包含需要保护的机密信息,则应使用分布式组件对象模型 (DCOM)、安全套接字层 (SSL)、安全 Internet 协议 (IPSec) 等安全通信技术。
如果数据是加密存储在数据库中,则通常由数据访问逻辑组件负责数据的加密与解密。如果信息暴露会导致巨大损害,则必须考虑保护与数据访问逻辑组件进行通信的通道。
业务实体组件中的安全性建议
如果将业务实体实现为数据结构(如 XML 或 DataSet),则不需要实现安全性检查。然而,如果将业务实体实现为带有 CRUD 操作的自定义业务实体组件,请考虑以下建议:
如果将实体提供给您不完全信任的业务过程,应在业务实体组件和数据访问逻辑组件中实现授权检查。然而,如果在这两个层次上都实现检查,可能会产生保持安全性策略同步的维护问题。
业务实体组件不应处理通信安全性或数据加密,应把这些任务留给相应的数据访问逻辑组件。
部署
本节提供一些建议以帮助您确定如何部署数据访问逻辑组件和业务实体组件。
部署数据访问逻辑组件
部署数据访问逻辑组件的方法有两种:
与业务过程对象一起部署数据访问逻辑组件。这种部署方法具有最佳的数据传输性能,还有一些额外的技术优势:
- 事务处理可以在业务过程对象和数据访问逻辑组件之间无缝流动。然而,事务处理不能跨越远程通道无缝流动。在远程方案下,需要使用 DCOM 来实现事务处理。此外,如果业务过程与数据访问逻辑组件被防火墙分开,还需要打开这两个物理层之间的防火墙端口以启用 DTC 通信。
- 一起部署业务过程对象和数据访问逻辑组件可以减少事务处理失败节点的数目。
安全性环境自动在业务过程对象和数据访问逻辑组件之间流动,无需设置当事人对象。
与用户界面代码一起部署数据访问逻辑组件。有时需要直接从 UI 组件和 UI 过程组件使用数据访问逻辑组件。为提高 Web 方案下的性能,可以与 UI 代码一起部署数据访问逻辑组件;这种部署方法可以使 UI 层充分利用数据读取器流以获得最佳性能。然而,在使用这种部署方法时必须牢记以下事项:
- 不与 UI 代码一起部署数据访问逻辑组件的一个常见原因是防止通过 Web 领域直接对数据源进行网络访问。
- 如果您的 Web 领域部署在 DMZ 环境中,则必须打开防火墙端口才能访问 SQL Server。如果使用 COM+ 事务处理,还必须为 DTC 通信打开其他的防火墙端口。
部署业务实体
应用程序的许多不同层都要使用业务实体。根据业务实体的实现方式,如果您的应用程序跨越各个物理层,则需要将业务实体部署到多个位置。下面列出了在不同实现方案中部署业务实体的方法:
部署作为有类型的 DataSet 实现的业务实体。有类型的 DataSet 类必须由数据访问逻辑组件和调用应用程序访问。因此,建议在一个要部署在多个层的公共程序集中定义有类型的 DataSet 类。
部署作为自定义业务实体组件实现的业务实体。根据数据访问逻辑组件中定义的方法签名,自定义实体类可能需要由数据访问逻辑组件访问。请遵循与有类型的 DataSet 相同的建议,即在一个要部署在多个层的公共程序集中定义自定义实体类。
部署作为通用 DataSet 或 XML 字符串实现的业务实体。通用 DataSet 和 XML 字符串不表示单独的数据类型。以这两种格式实现的业务实体不存在部署问题。 |