| 本章内容 | |
| 目标 | |
| 适用范围 | |
| 如何使用本章内容 | |
| .NET Remoting 体系结构 | |
| .NET Remoting 网关守卫 | |
| 身份验证 | |
| 授权 | |
| 身份验证和授权策略 | |
| 访问系统资源 | |
| 访问网络资源 | |
| 将身份验证的凭据传递给远程对象 | |
| 传递原调用方 | |
| 受信任的子系统 | |
| 安全通信 | |
| 选择主机进程 | |
| 远程处理与 Web 服务的比较 | |
| 总结 |
.NET Framework 提供了远程处理的基础结构,这样,客户端就可以与驻留在远程应用程序域或进程中的对象或远程计算机上的对象通信。在分布式 Web 应用程序中,.NET Remoting 通常用作一种机制,供表示层组件访问位于中间层的业务逻辑组件。
本章说明如何在使用 .NET Remoting 的分布式 Web 应用程序中实现身份验证、授权和安全通信。
本章的目标是:
| • | 实施安全的 .NET Remoting 解决方案。 |
| • | 生成安全的远程组件。 |
| • | 了解使用 .NET Remoting 的分布式应用程序的体系结构和操作,并熟悉 .NET Remoting 的可扩展性功能。 |
| • | 选择适当的主机进程。 |
| • | 标识并使用 .NET Remoting 提供的网关守卫。 |
| • | 将带有调用的客户端凭据传递给远程对象。 |
| • | 将原调用方的标识从远程组件传递给下游系统。 |
| • | 了解何时使用 .NET Remoting 以及何时使用 Web 服务作为您的分布式 Web 应用程序的一部分。 |
| • | 为您的 .NET Remoting 解决方案设计正确的身份验证、授权和安全通信机制。 |
本章适用于以下产品和技术:
| • | Windows XP 或 Windows 2000 Server (Service Pack 3) 和更高版本的操作系统 |
| • | Microsoft Internet 信息服务 (IIS) 5.0 和更高版本 |
| • | Microsoft Active Directory |
| • | .NET Framework 版本 1.0 (Service Pack 2) 和更高版本 |
| • | Visual C# .NET |
| • | SQL Server 2000 (Service Pack 2) 和更高版本 |
若要学好本章内容:
| • | 您必须具有使用 Visual C# .NET 进行编程的经验。 | ||||||||||||
| • | 您必须具有开发和配置 ASP.NET Web 应用程序的经验。 | ||||||||||||
| • | 您必须具有配置 SQL Server 安全性、IIS 安全性、Windows 安全性和 Active Directory 的经验。 | ||||||||||||
| • | 阅读第 1 章简介。这一章说明了身份验证、授权和安全通信对于分布式 Web 应用程序的重要性。 | ||||||||||||
| • | 阅读第 2 章 ASP.NET 应用程序的安全模型。这一章概述了创建分布式 ASP.NET Web 应用程序所采用的体系结构和技术,并重点说明了身份验证、授权和安全通信在该体系结构中适用的位置。 | ||||||||||||
| • | 阅读第 3 章身份验证和授权。这一章详细介绍了使用 .NET Remoting 时可用的身份验证机制和授权机制。 | ||||||||||||
| • | 阅读第 4 章安全通信。这一章介绍了 SSL 和 IPSec,您将经常使用它们来保护客户端和远程对象之间的通信信道的安全。 | ||||||||||||
| • | 阅读第 8 章 ASP.NET 安全性。这一章深入讨论了与 ASP.NET 相关的安全性问题,您在 ASP.NET 中承载远程对象(建议的方式)时会遇到许多此类问题。 | ||||||||||||
| • | 请阅读以下章节,其中介绍了如何实现本章所述的许多技术:
|
图 11.1 显示了在 ASP.NET 中驻留远程对象时的基本 .NET Remoting 体系结构。如果主要关心安全性问题,那么建议使用 ASP.NET 主机和 HTTP 通道进行通信,因为它允许远程对象利用 ASP.NET 和 IIS 提供的基本安全服务。
有关可能使用的主机和通道类型范围的详细信息以及比较信息,请参见本章后面的选择主机进程。

图 11.1
.NET Remoting 体系结构
客户端与进程内的代理对象进行通信。可以通过远程对象代理设置身份验证凭据(例如,用户名、密码和证书等等)。方法调用通过接收链进行传递(您可以实现自己的自定义接收来执行数据加密),并到达负责通过网络发送数据的传输接收。在服务器端,调用通过相同的管道进行传递,并向对象发出调用。
注意:本章中使用的术语代理一词是指客户端进程内的代理对象,客户端通过该对象与远程对象进行通信。不要将它与术语代理服务器混淆。
当客户端在远程对象上进行方法调用时,.NET Remoting 使用传输通道接收、自定义通道接收和格式化程序通道接收。
传输通道接收通过网络在客户端与服务器之间传递方法调用。.NET 提供了 HttpChannel 和 TcpChannel 类,但是,可以对体系结构进行完全扩展,并且插入您自己的自定义实现方法。
| • | HttpChannel。在将远程对象驻留在 ASP.NET 中时,可以使用此通道。此通道使用 HTTP 协议在客户端和服务器之间发送消息。 |
| • | TcpChannel。在将远程对象驻留在 Microsoft® Windows® 操作系统服务或其他可执行文件中时,可以使用此通道。此通道使用 TCP 套接字在客户端和服务器之间发送消息。 |
| • | 自定义通道。自定义的传输通道可以使用任何基本的传输协议在客户端和服务器之间发送消息。例如,自定义通道可以使用命名管道或邮件槽。 |
比较传输通道接收
下表对两个主要的传输通道接收进行了比较。
表 11.1: TcpChannel 和 HttpChannel 的比较
| 功能 | TCP 通道 | HTTP 通道 | 注释 |
身份验证 | 否 | 是 | HTTP 通道使用 |
授权 | 否 | 是 | HTTP 通道支持 |
安全通信 | 是 | 是 | 在 TCP 通道中使用 IPSec。 |
可以在通道接收管道内的不同位置上使用自定义通道接收来修改在客户端和服务器之间发送的消息。提供加密和解密功能的通道接收就是一个自定义通道接收的示例。
格式化程序接收获取方法调用,并将它们序列化为可通过网络发送的流。.NET 提供两种格式化程序接收:
| • | 二进制格式化程序。它使用 BinaryFormatter 类将方法调用打包为一个序列化的二进制流,然后传递该流(使用 HTTP POST)以便向服务器发送数据。二进制格式化程序将 HTTP 请求中的内容类型设置为“application/octet-stream”。 |
| • | SOAP 格式化程序。它使用 SoapFormatter 类将方法调用打包为 SOAP 消息。在 HTTP 请求中将内容类型设置为“text/xml”,并使用 HTTP POST 将其传递到服务器。 |
远程对象终结点的地址是由以 .rem 或 .soap 扩展文件名结尾的 URL 指定的(例如 http://someserver/vDir/remoteobject.soap)。当 IIS 收到远程对象(带有 .rem 或 .soap 扩展名)的请求时,就会将它映射到 ASP.NET ISAPI 扩展 (Aspnet_isapi.dll) 中。ISAPI 扩展将该请求转发到 ASP.NET 辅助进程 (Aspnet_wp.exe) 中的应用程序域。图 11.2 显示了事件的顺序。

图 11.2
服务器端处理
图 11.2 显示了以下事件顺序:
1. | 通过 HTTP 接收 .soap 或 .rem 请求,并将其映射到 Web 服务器上的特定虚拟目录。 |
2. | IIS 检查 .soap/.rem 映射,然后将文件扩展名映射到 ASP.NET ISAPI 扩展 Aspnet_isapi.dll 中。 |
3. | ISAPI 扩展将请求传递给 ASP.NET 辅助进程 (Aspnet_wp.exe) 中的应用程序域。如果这是在该应用程序上发送的第一个请求,则创建一个新的应用程序域。 |
4. | 调用 HttpRemotingHandlerFactory 处理程序,然后远程处理基础结构读取 Web.config 文件中的 <system.runtime.remoting> 部分,它控制服务器端的对象配置(例如,单个调用或单个参数)和授权参数(使用 <authorization> 元素)。 |
5. | .NET Remoting 基础结构查找包含远程对象的程序集并对其进行实例化。 |
6. | .NET Remoting 基础结构读取 HTTP 标头和数据流,然后在远程对象上调用该方法。 注意:在此进程中,ASP.NET 调用正常的事件处理程序序列。您可以有选择地在 Global.asax 中实现一个或多个事件处理程序,例如 BeginRequest、AuthenticationRequest、AuthorizeRequest 等等。在请求到达远程对象方法时,将代表已验证身份的用户的 IPrincipal 对象存储在 HttpContext.User (和 Thread.CurrentPrincipal)中,并且可以将它用于授权。例如,使用主体权限要求和编程角色检查。 |
.NET Remoting 没有自己的安全模型。客户端(代理)和服务器(远程对象)之间的身份验证和授权是通过通道和主机进程执行的。可以组合使用以下主机和通道:
| • | 自定义的可执行文件和 TCP 通道。这种组合不提供任何内置的安全功能。 |
| • | ASP.NET 和 HTTP 通道。这种组合通过基本的 ASP.NET 和 IIS 安全功能提供身份验证和授权。 |
ASP.NET 中驻留的对象可以利用 ASP.NET 和 IIS 的基本安全功能。它们包括:
| • | 身份验证功能。在 Web.config 中配置 Windows 身份验证功能: <authentication mode="Windows"/> IIS 中的设置控制着所使用的 HTTP 身份验证类型。 通用的 HTTP 标头用于对请求进行身份验证。可通过配置远程对象代理为客户端提供凭据,也可以使用默认凭据。 不能使用窗体或 Passport 身份验证,因为通道没有提供允许客户端访问 Cookie 的方法,而这是这两种身份验证机制的一个要求。另外,窗体和 Passport 身份验证需要重定向到要求客户端交互的登录页面。远程的服务器端对象可用于非交互式的场合。 |
| • | 授权功能。使用标准的 ASP.NET 授权方法对客户端进行授权。 可配置的授权选项包括:
编程授权选项包括:
| ||||||||
| • | 安全通信功能。应该使用 SSL(和/或 IPSec)保护客户端和服务器之间数据传输的安全。 |
| • | 有关 ASP.NET 和 IIS 提供的身份验证和授权功能的详细信息,请参见第 8 章 ASP.NET 安全性。 |
| • | 有关如何在 ASP.NET/IIS 中驻留对象的详细信息,请参见 Microsoft 知识库文章 Q312107 HOW TO: Host a Remote Object in Microsoft Internet Information Services(如何在 Microsoft Internet 信息服务中驻留远程对象)。 |
ASP.NET 承载的远程对象使用的授权点(或者网关守卫)包括:
| • | IIS。如果关闭了匿名身份验证,则 IIS 只接收来自特定用户的请求,即它可以在自己的域或受信任的域中验证这些用户的身份。IIS 还提供 IP 地址和 DNS 筛选功能。 | ||||
| • | ASP.NET
| ||||
| • | 主体权限要求和明确的角色检查。除了使用 IIS 和 ASP.NET 可配置的网关守卫外,还可以将主体权限要求(以声明方式或强制方式)用作附加的细分访问控制机制。通过使用主体权限检查,您可以根据各个用户的标识和组成员身份(由附加到当前线程的 IPrincipal 对象定义)控制对类、方法或个别代码块的访问。 注意:用于请求角色成员身份的主体权限要求与调用 IPrincipal.IsInRole 来测试角色成员身份不同。如果调用方不是指定角色的成员,则前者产生异常,而后者仅返回一个布尔值以确认角色成员身份。 在 Windows 身份验证中,ASP.NET 自动将一个代表已进行身份验证的用户的 WindowsPrincipal 对象附加到当前的 Web 请求(使用 HttpContext.User)。 |
在结合使用远程处理和 ASP.NET Web 应用程序客户端时,就会在 Web 应用程序和远程对象主机上进行身份验证。远程对象主机可以使用的身份验证选项取决于主机类型。
当对象驻留在 ASP.NET 中时,可以使用 HTTP 通道在客户端代理和服务器之间传递方法调用。HTTP 通道使用 HTTP 协议对服务器的远程对象代理进行身份验证。
以下列表说明在 ASP.NET 中驻留对象时可以使用的一组身份验证选项:
| • | IIS 身份验证选项:匿名、基本、摘要式、Windows 集成和证书。 |
| • | ASP.NET 身份验证选项:Windows 身份验证或无(用于自定义的身份验证实现)。 注意:.NET Remoting 不能直接使用窗体和 Passport 身份验证。远程对象调用用于非交互式场合。如果远程对象的客户端是 .NET Web 应用程序,则 Web 应用程序可以使用窗体身份验证和 Passport 身份验证,并将凭据明确地传递给远程对象。在本章后面的传递原调用方一节中将对这种类型的方案进一步进行讨论。 |
当对象驻留在 Windows 服务中时,可以使用 TCP 通道在客户端和服务器之间传递方法调用。它使用基于套接字的原始通信方法。因为套接字没有提供身份验证,所以服务器无法对客户端进行身份验证。
在此方案中,远程对象必须使用自定义身份验证。
对于简单的自定义身份验证,远程对象可以公开一个接受用户名和密码的 Login 方法。可以根据存储、检索的角色列表以及发回到客户端的令牌对凭据进行验证,以便在后续请求中使用。在服务器上检索到令牌后,可以使用它来创建一个存储在 Thread.CurrentPrincipal 中的 IPrincipal 对象(包含角色)以便进行授权。
其他自定义身份验证的示例还包括:创建使用提供身份验证的进程间通信通道的自定义传输通道接收(例如,命名管道),或者创建使用 Windows 安全服务提供程序接口 (SSPI) 执行身份验证的通道接收。
更多信息
| • | 有关如何在 Windows 服务中承载对象的信息,请参见本指南中的如何在 Windows 服务中承载远程对象。 |
| • | 有关接收和接收链的详细信息,请搜索 MSDN 库 .NET Framework 部分中的 Sinks and Sink Chains(接收和接收链)。 |
| • | 有关如何创建使用 SSPI 的自定义身份验证解决方案的详细信息,请参见以下 MSDN 文章“.NET Remoting Security Solution, Part 1: Microsoft.Samples.Security.SSPI Assembly”(.NET Remoting 安全解决方案第一部分:Microsoft.Samples.Security.SSPI 程序集):http://msdn.microsoft.com/library/en-us/dndotnet/html/remsspi.asp。 注意:该文中所述的实现过程是一个示例,而不是经过 Microsoft 测试并得到支持的产品。 |
当在 ASP.NET 中驻留对象并且使用 HTTP 通道进行通信时,可以对客户端进行身份验证,并使用以下机制来控制授权:
| • | URL 授权 |
| • | 文件授权 |
| • | 主体权限要求(声明方式和强制方式) |
| • | 代码中的 IPrincipal.IsInRole 检查 |
当在 Windows 服务中驻留对象时,TCP 通道不提供任何身份验证。因此,您必须通过创建 IPrincipal 对象并将其存储在 Thread.CurrentPrincipal 中,来执行自定义身份验证并进行授权。
然后,可以使用声明方式的主体权限要求检查对远程对象的方法进行注释(如下所示):
[PrincipalPermission(SecurityAction.Demand,
Role="Manager")]
void SomeMethod()
{
}
在对象的方法代码中,也可以使用强制方式的用户权限要求,并可利用 IPrincipal.IsInRole 进行明确的角色检查。
您可能需要使用内置的 Windows 访问控制将远程对象作为可保护的 Windows 资源保护起来。如果没有文件授权(使用 Windows ACL),则您只能使用 URL 授权。
若要使用 FileAuthorizationModule 授权访问远程对象终结点(用 .rem 或 .soap URL 标识),您必须在应用程序的虚拟目录中创建带有 .rem 或 .soap 扩展名的物理文件。
注意:IIS 使用 .rem 和 .soap 扩展名将对象终结点的请求映射到 ASP.NET ISAPI 扩展 (aspnet_isapi.dll)。通常,它们并不是作为物理文件存在。
为 .NET Remoting 配置文件授权:
1. | 在应用程序虚拟目录的根目录中,创建与 objectUri 具有相同名称的文件(例如,RemoteMath.rem)。 |
2. | 在该文件的顶部添加以下行,然后保存该文件: <%@ webservice class="YourNamespace.YourClass" ... %> |
3. | 使用 Windows 资源管理器将适当配置的 ACL 添加到该文件中。 注意:可以从用于在服务器上配置远程对象的 web.config 文件中获取 objectUri。查找 <wellknown> 元素,如下例所示: <wellknown mode="SingleCall" objectUri="RemoteMath.rem" type="RemotingObjects.RemoteMath, RemotingObjects, Version=1.0.000.000 Culture=neutral, PublicKeyToken=4b5ae668c251b606"/> |
更多信息
| • | 有关这些授权机制的详细信息,请参见第 8 章 ASP.NET 安全性。 |
在许多使用 .NET Remoting 的应用程序中,可以使用远程对象在应用程序中间层中提供业务功能,并且 ASP.NET Web 应用程序可以调用该功能。图 11.3 中显示了这种设置。

图 11.3
ASP.NET Web 应用程序调用的远程对象
在此方案中,使用为 Web 应用程序提供的 IIS 和 ASP.NET 网关守卫来保护对客户端代理的访问;而使用为远程应用程序服务器上 ASP.NET 主机提供的 IIS 和 ASP.NET 网关守卫来保护对远程对象的访问。
.NET Web 应用程序访问的远程对象可以使用两种基本的身份验证和授权策略。
| • | 可以在 Web 服务器上对调用方进行身份验证和授权,然后使用模拟将调用方的安全性上下文传递给远程对象。这就是模拟/委派模型。 可以使用 ASP.NET 可配置和编程的网关守卫(包括 URL 授权、文件授权、主体权限要求以及 .NET 角色)在远程对象上对各个调用方进行授权。 |
| • | 您可以在 Web 服务器对调用方进行身份验证和授权,然后使用受信任的标识与远程对象进行通信。这就是受信任的子系统模型。 |
更多信息
| • | 有关模拟/委派模型和受信任的子系统模型的详细信息,请参见第 3 章身份验证和授权中授权方法一节的“资源访问模型”主题。 |
| • | 有关在远程处理过程中使用原调用方模型的详细信息,请参见本章后面的传递原调用方。 |
| • | 有关在远程处理过程中使用受信任的子系统模型的详细信息,请参见本章后面的受信任的子系统。 |
有关从 ASP.NET 中驻留的远程对象访问系统资源(例如,事件日志和注册表)的详细信息,请参见第 8 章 ASP.NET 安全性中的访问系统资源。第 8 章中介绍的方法和限制也适用于 ASP.NET 中驻留的远程对象。
在从远程对象访问网络资源时,您需要考虑用于响应来自远程计算机的网络身份验证质询的标识。您有三个选项:
| • | 进程标识(这是默认标识)。如果在 ASP.NET 中驻留对象,则用于运行 ASP.NET 辅助进程并由 Machine.config 中 <processModel> 元素定义的标识决定了用于资源访问的安全性上下文。 |
| • | 固定服务标识。例如,通过调用 LogonUser 创建的标识。 注意:不要将此服务标识与用来运行 Windows 服务的标识混淆起来。固定服务标识是指专为从应用程序访问资源而创建的 Windows 用户帐户。 |
| • | 原调用方标识。将 ASP.NET 配置为使用模拟功能,或者在 Windows 服务中使用编程模拟。 |
有关每种方法相对优点的详细信息,请参见第 8 章 ASP.NET 安全性中的访问网络资源。
在客户端进程调用远程对象时,它使用代理来完成这一操作。这是一个本地对象,它公开一组与目标对象相同的方法。
如果远程对象驻留在 ASP.NET 中并被配置为使用 Windows 身份验证,则必须使用通道的凭据属性来指定用于身份验证的凭据。如果没有明确设置凭据,就会在没有任何凭据的情况下调用远程对象。如果需要使用 Windows 身份验证,这将导致出现 HTTP 错误状态 401,即拒绝访问。
如果您要使用驻留远程对象代理(如果调用代理的线程使用模拟功能,则为当前线程令牌)的进程的凭据,则应该将通道的凭据属性设置为进程凭据缓存保存的 DefaultCredentials。
可以在配置文件中指定是否使用 DefaultCredentials,或者通过编程方式设置凭据。
显式配置
在客户端应用程序配置文件(如果客户端应用程序是 ASP.NET Web 应用程序,则为 Web.config)中,将 <channel> 元素的 useDefaultCredentials 属性设置为 true,指定代理在与远程对象进行通信时应该使用 DefaultCredentials。
<channel ref="http" useDefaultCredentials="true" />
编程配置
对于编程配置,请使用以下代码以编程方式确定是否使用 DefaultCredentials:
IDictionary channelProperties; channelProperties = ChannelServices.GetChannelSinkProperties(proxy); channelProperties ["credentials"] = CredentialCache.DefaultCredentials;
在调用远程对象时,若要使用一组特定的身份验证凭据,请使用以下设置禁止在配置文件中使用默认凭据:
<channel ref="http" useDefaultCredentials="false" />
注意:编程设置会始终覆盖配置文件中的设置。
然后,使用以下代码来配置代理以使用特定的凭据:
IDictionary channelProperties =
ChannelServices.GetChannelSinkProperties(proxy);
NetworkCredential credentials;
credentials = new NetworkCredential("username", "password", "domain");
ObjRef objectReference = RemotingServices.Marshal(proxy);
Uri objectUri = new Uri(objectReference.URI);
CredentialCache credCache = new CredentialCache();
// 使用“协商”、“基本”、“摘要式”、“Kerberos”或“NTLM”
// 替代“authenticationType”
credCache.Add(objectUri, "authenticationType", credentials);
channelProperties["credentials"] = credCache;
channelProperties["preauthenticate"] = true;
请求特定的身份验证类型
按照上面所述,您应该使用 CredentialCache.Add 方法来请求特定的身份验证类型。避免直接使用 NetworkCredential 类,如以下代码所示:
IDictionary providerData = ChannelServices.GetChannelSinkProperties(yourProxy); providerData["credentials"] = new NetworkCredential(uid, pwd);
在生产代码中应该避免出现这种情况,因为您无法控制远程对象主机使用的身份验证机制,因此也无法控制使用凭据的方式。
例如,您可能期望得到来自服务器的 Kerberos 或 NTLM 验证要求,但实际却可能收到一个基本身份验证要求。在这种情况下,以明文形式将提供的用户名和密码发送到服务器。
可以将代理的 preauthenticate 属性设置为 true 或 false。如果将其设置为 true(如上述代码所示),则可以提供特定的身份验证凭据,以便通过初始请求传递 WWW-Authenticate HTTP 标头。这可避免 Web 服务器拒绝初始请求的访问,并在后续请求中执行身份验证。
如果 ASP.NET Web 应用程序连接到(驻留在 ASP.NET 中)远程组件并传递原调用方的安全性上下文(通过使用 DefaultCredentials 和模拟或通过设置显式凭据,如上所示),则应该在 Web 应用程序中设置通道的 connectiongroupname 属性。这用于防止未验证身份的新客户端重用到远程组件(该组件与以前的客户端身份验证凭据关联)的已验证身份的旧连接。在使用 HTTP KeepAlives 或出于 IIS 的性能考虑而启用身份验证持续功能时,会出现连接重用。
将 connectiongroupname 属性设置为能够区分调用方的标识符(例如,调用方的用户名)。
channelProperties["connectiongroupname"] = userName;
注意:如果没有通过 Web 应用程序将原调用方的安全性上下文传递给远程组件,而是使用固定标识(例如,Web 应用程序的 ASP.NET 进程标识)连接到远程组件,则您不需要设置 connectiongroupname 属性。在此方案中,连接安全性上下文在从一个调用方传递到下一个调用方时,保持不变。
下一版本的 .NET Framework 将支持基于调用代理对象的线程 SID 的连接池,如果 Web 应用程序正在模拟调用方,则它有助于解决上述问题。.NET Remoting 客户端支持连接池,但 Web 服务客户端不支持。
本节介绍如何通过 ASP.NET Web 应用程序将原调用方的安全性上下文传递给远程应用程序服务器上 ASP.NET 驻留的远程组件。要在远程对象或后续下游子系统(例如,数据库)中支持每用户的授权,您可能需要完成上述操作。
在图 11.4 中,通过驻留在 ASP.NET Web 应用程序的前端 Web 服务器将原调用方(李华)的安全性上下文传递给远程应用程序服务器上 ASP.NET 中驻留的远程对象,并最终将其传递到后端数据库服务器。

图 11.4
传递原调用方的安全性上下文
为了向远程对象传递凭据,远程对象客户端(此方案中的 ASP.NET Web 应用程序)必须配置该对象的代理,并明确地设置代理的 credentials 属性。有关说明,请参见本章前面的将身份验证的凭据传递给远程对象。
注意:IPrincipal 对象无法跨越 .NET Remoting 边界进行传递。
可以使用两种方法来传递调用方的上下文:
| • | 传递默认凭据并使用 Kerberos 身份验证(和委派)。该方式要求您在 ASP.NET Web 应用程序内使用模拟,并用从被模拟调用方的安全性上下文中获得的 DefaultCredentials 来配置远程对象代理。 |
| • | 传递显式凭据并使用基本身份验证或窗体身份验证。该方式不要求在 ASP.NET Web 应用程序内使用模拟。相反,您可以通过编程方式,使用为 Web 应用程序提供的服务器变量(利用基本身份验证)或 HTML 窗体字段(利用窗体身份验证)中获取的显式凭据来配置远程对象代理。可以使用基本身份验证或窗体身份验证,以明文形式为服务器提供用户名和密码。 |
要使用 Kerberos 委派,所有计算机(服务器和客户端)都必须运行 Windows 2000 或更高版本。此外,必须将要委派的客户端帐户存储在 Active Directory® 目录服务中,并且不能将其标记为“敏感帐户,不能被委派”。
以下各表显示了在 Web 服务器和应用程序服务器上需要执行的配置步骤。
| 配置 IIS | |
| 步骤 | 更多信息 |
禁用对 Web 应用程序的虚拟根目录的匿名访问 |
|
对 Web 应用程序的虚拟根目录启用 Windows 集成身份验证 | 如果客户端和服务器运行的是 Windows 2000 或更高版本,则可以对 Kerberos 身份验证进行协商。 |
| 配置 ASP.NET | |
| 步骤 | 更多信息 |
将 ASP.NET Web 应用程序配置为使用 Windows 身份验证 | 编辑 Web 应用程序的虚拟目录下的 Web.config 文件。 <authentication mode="Windows" /> |
配置 ASP.NET Web 应用程序的模拟 | 编辑 Web 应用程序的虚拟目录下的 Web.config 文件。 <identity impersonate="true" /> |
| 配置远程处理(客户端代理) | |
| 步骤 | 更多信息 |
配置远程对象代理,以便将默认凭据用于对远程对象的所有调用 | 将如下条目添加到 Web.config 文件中: <channel ref="http" useDefaultCredentials="true" /> 将从 Web 应用程序的 |
| 配置 IIS | |
| 步骤 | 更多信息 |
禁用对 Web 应用程序的虚拟根目录的匿名访问 对 Web 应用程序的虚拟根目录启用 Windows 集成身份验证 |
|
| 配置 ASP.NET(远程对象主机) | |
| 步骤 | 更多信息 |
将 ASP.NET 配置为使用 Windows 身份验证 | 编辑该应用程序的虚拟目录下的 Web.config 文件。将 <authentication> 元素设置为: <authentication mode="Windows" /> |
配置 ASP.NET 的模拟功能 | 编辑该应用程序的虚拟目录下的 Web.config 文件。将 <identity> 元素设置为: <identity impersonate="true" /> 注意:只有在您要将原调用方的安全性上下文通过远程对象传递给下一个下游子系统(例如数据库)时,才需要执行本步骤。如果在此处启用了模拟功能,资源访问(本地和远程)就会使用模拟的原调用方的安全性上下文。如果您只要求在远程对象中对每个用户进行授权检查,则无需在此处启用模拟功能。 |
更多信息
有关 Kerberos 委派的详细信息,请参见本指南中的如何为 Windows 2000 实现 Kerberos 委派。
作为 Kerberos 委派的一种替代方法,您可以在 Web 应用程序中使用基本身份验证或窗体身份验证来捕获客户端凭据,然后对远程对象使用基本身份验证(或者集成 Windows 身份验证)。
利用此方式,Web 应用程序可以使用客户端的明文凭据。可以通过远程对象代理将这些凭据传递给远程对象。为此,必须在 Web 应用程序中包含代码以检索客户端凭据并配置远程对象代理。
使用基本身份验证,Web 应用程序可在服务器变量中使用原调用方的凭据。以下代码说明如何检索这些凭据并配置远程对象代理:
// 检索客户端的凭据(可通过基本身份验证进行)
string pwd = Request.ServerVariables["AUTH_PASSWORD"];
string uid = Request.ServerVariables["AUTH_USER"];
// 将凭据与远程对象代理相关联
IDictionary channelProperties =
ChannelServices.GetChannelSinkProperties(proxy);
NetworkCredential credentials;
credentials = new NetworkCredential(uid, pwd);
ObjRef objectReference = RemotingServices.Marshal(proxy);
Uri objectUri = new Uri(objectReference.URI);
CredentialCache credCache = new CredentialCache();
credCache.Add(objectUri, "Basic", credentials);
channelProperties["credentials"] = credCache;
channelProperties["preauthenticate"] = true;
注意:上述代码中的 NetworkCredential 构造函数是随用户 ID 和密码提供的。要避免对域名进行硬编码,在配置基本身份验证时,可以在 IIS 中的 Web 服务器上配置默认域。
使用窗体身份验证,Web 应用程序可在窗体各个字段中(而不是在服务器变量中)使用原调用方的凭据。在这种情况下,请使用以下代码:
// 从登录窗体检索客户端的凭据
string pwd = txtPassword.Text;
string uid = txtUid.Text;
// 将凭据与远程对象代理相关联
IDictionary channelProperties =
ChannelServices.GetChannelSinkProperties(proxy);
NetworkCredential credentials;
credentials = new NetworkCredential(uid, pwd);
ObjRef objectReference = RemotingServices.Marshal(proxy);
Uri objectUri = new Uri(objectReference.URI);
CredentialCache credCache = new CredentialCache();
credCache.Add(objectUri, "Basic", credentials);
channelProperties["credentials"] = credCache;
channelProperties["preauthenticate"] = true;
以下各表显示了在 Web 服务器和应用程序服务器上需要执行的配置步骤。
| 配置 IIS | |
| 步骤 | 更多信息 |
要使用基本身份验证,请禁用对您 Web 应用程序的虚拟根目录的匿名访问,并选择基本身份验证 -或- 要使用窗体身份验证,请启用匿名访问 | 基本身份验证和窗体身份验证都应和 SSL 配合使用,才能保护通过网络传送的明文凭据。如果您使用基本身份验证,则应该将 SSL 用于所有页面(而不仅仅是初始登录页面),因为在每个请求中都传递基本身份验证凭据。类似地,如果您使用窗体身份验证,则应该将 SSL 用于所有页面,以保护初始登录中的明文凭据以及在后续请求中传递的身份验证票证。 |
| 配置 ASP.NET | |
| 步骤 | 更多信息 |
如果您使用基本身份验证,则将您的 ASP.NET Web 应用程序配置为使用 Windows 身份验证 -或- 如果您使用窗体身份验证,则将您的 ASP.NET Web 应用程序配置为使用窗体身份验证 | 编辑您的 Web 应用程序的虚拟目录下的 Web.config 文件。将 <authentication> 元素设置为: <authentication mode="Windows" /> -或- 编辑您的 Web 应用程序的虚拟目录下的 Web.config 文件。将 <authentication> 元素设置为: <authentication mode="Forms" /> |
禁用 ASP.NET Web 应用程序内的模拟功能 | 编辑您的 Web 应用程序的虚拟目录下的 Web.config 文件。将 <identity> 元素设置为: <identity impersonate="false" /> 注意:这相当于没有 <identity> 元素。因为通过远程对象代理将用户凭据明确传递给远程对象,所以不需要使用模拟功能。 |
| 配置远程处理(客户端代理) | |
| 步骤 | 更多信息 |
配置远程处理代理,不要将默认凭据用于对远程对象的所有调用 | 将如下条目添加到 Web.config 文件中:
<channel ref="http"
useDefaultCredentials="false" />
|
您不希望使用默认凭据(因为将 Web 应用程序配置为不使用模拟,因此,这将导致使用 ASP.NET 进程标识的安全性上下文)。 | |
编写代码,以便在远程对象代理中捕获和明确地设置凭据 | 请参考前面的代码片段。 |
| 配置 IIS | |
| 步骤 | 更多信息 |
禁用对应用程序的虚拟根目录的匿名访问 |
|
启用基本身份验证 | 注意:通过在应用程序服务器(远程对象)上使用基本身份验证,远程对象可以将原调用方的安全性上下文传递到数据库(因为调用方的用户名和密码采用明文形式,并且可以用来响应来自数据库服务器的网络身份验证质询)。如果不需要将原调用方的安全性上下文传递到远程对象以外,则考虑在应用程序服务器上将 IIS 配置为使用 Windows 集成身份验证,因为这可提供更高的安全性(不通过网络传递凭据,并且不给应用程序提供凭据)。 |
| 配置 ASP.NET(远程对象主机) | |
| 步骤 | 更多信息 |
将 ASP.NET 配置为使用 Windows 身份验证 | 编辑该应用程序的虚拟目录下的 Web.config 文件。将 <authentication> 元素设置为: <authentication mode="Windows" /> |
配置 ASP.NET 的模拟功能 | 编辑该应用程序的虚拟目录下的 Web.config 文件。将 <identity> 元素设置为: <identity impersonate="true" /> 注意:只有在您要将原调用方的安全性上下文通过远程对象传递给下一个下游子系统(例如数据库)时,才需要执行本步骤。如果在此处启用了模拟功能,资源访问(本地和远程)就会使用模拟的原调用方的安全性上下文。如果您只要求在远程对象中对每个用户进行授权检查,则无需在此处启用模拟功能。 |
受信任的子系统模型提供了一种替代(且更易于实现的)方式来传递原调用方的安全性上下文。在该模型中,远程对象主机和 Web 应用程序之间存在信任边界。远程对象首先信任 Web 应用程序对调用方正确地进行身份验证和授权,然后才允许向远程对象发送请求。在远程对象主机中不对原调用方进行任何身份验证。远程对象主机对 Web 应用程序用来与远程对象通信的固定受信任标识进行身份验证。在大多数情况下,这是 ASP.NET Web 应用程序的进程标识。
图 11.5 显示了受信任的子系统模型。该图还显示了两种可能的配置。第一种配置使用 ASP.NET 主机和 HTTP 通道,而第二种配置使用 Windows 服务主机和 TCP 通道。

图 11.5
受信任的子系统模型
如果您使用的是受信任的子系统模型,则可能仍然需要传递原调用方的标识(名称,不是安全性上下文),例如,用于在数据库中进行审核。
通过使用可用于从数据库检索用户特定数据的方法、存储过程参数和受信任的查询参数(如下面的示例所示),可以在应用程序级传递该标识。
SELECT x,y,z FROM SomeTable WHERE UserName = "李华"
受信任的子系统模型意味着远程对象主机不对原调用方进行身份验证。但是,它仍需对其中间客户端(此方案中的 ASP.NET Web 应用程序)进行身份验证(和授权),以防止未经授权的应用程序向远程对象发出请求。
如果您在 ASP.NET 中驻留对象并使用 HTTP 通道,则可以使用 Windows 集成身份验证对 ASP.NET Web 应用程序进程标识进行身份验证。
如果您在 Windows 服务中驻留对象,则可以使用具有更高性能的 TCP 通道,但是该通道不提供身份验证功能。在此方案中,可以在 Web 服务器和应用程序服务器之间使用 IPSec。可以制订只允许 Web 服务器与应用程序服务器进行通信的 IPSec 策略。
以下各表显示了在 Web 服务器和应用程序服务器上需要执行的配置步骤。
| 配置 IIS | |
| 步骤 | 更多信息 |
配置 IIS 身份验证 | Web 应用程序可以使用任何形式的验证方法来验证原调用方的身份。 |
| 配置 ASP.NET | |
| 步骤 | 更多信息 |
配置身份验证并确保禁用模拟功能 | 编辑 Web 应用程序的虚拟目录下的 Web.config 文件。 <authentication mode="Windows|Forms|Passport" /> 将 <identity> 元素设置为: identity impersonate="false" /> (或者删除 <identity> 元素) |
重置用于运行 ASP.NET 的 ASPNET 帐户的密码,或者创建一个权限最少的域帐户来运行 ASP.NET,并在 Web.config 文件内的 <processModel> 元素上指定帐户详细信息 | 有关如何从 |
| 配置远程处理(客户端代理) | |
| 步骤 | 更多信息 |
配置远程处理代理,以便将默认凭据用于对远程对象的所有调用 | 将如下条目添加到 Web.config 文件中: <channel ref="http" useDefaultCredentials="true" /> 由于 Web 应用程序没有使用模拟,因此,如果使用 |
| 配置 IIS | |
| 步骤 | 更多信息 |
禁用对应用程序的虚拟根目录的匿名访问 启用 Windows 集成身份验证 |
|
| 配置 ASP.NET(远程对象主机) | |
| 步骤 | 更多信息 |
将 ASP.NET 配置为使用 Windows 身份验证 | 编辑该应用程序的虚拟目录下的 Web.config 文件。将 <authentication> 元素设置为: <authentication mode="Windows" /> |
禁用模拟功能 | 编辑该应用程序的虚拟目录下的 Web.config 文件。将 <identity> 元素设置为: <identity impersonate="false" /> |
如果使用的是 Windows 服务主机进程,则必须创建一个 Windows 帐户以运行该服务。远程对象将使用该帐户提供的安全性上下文来访问所有本地和远程资源。
要访问远程 Microsoft SQL Server 数据库(利用 Windows 身份验证),您可以使用权限最少的域帐户;也可以使用本地帐户,然后在数据库服务器上创建一个重复帐户(具有相同的用户名和密码)。
安全通信是指在通过网络传送消息时确保消息的完整性和机密性。可以使用基于平台的方法来保护通信的安全并使用 SSL 或 IPSec;也可以使用消息级方法并开发自定义的加密接收,以便对整个消息或消息的选定部分进行加密。
可以考虑两个平台级选项以保护在客户端和远程组件之间传送的数据安全:
| • | SSL |
| • | IPSec |
如果远程对象驻留在 ASP.NET 中,则可以使用 SSL 来保护客户端和服务器之间的通信通道。这要求驻留远程对象的计算机具有服务器身份验证证书。
如果远程对象驻留在 Windows 服务中,则可以在客户端和主机(服务器)之间使用 IPSec,也可以开发自定义的加密接收。
由于 .NET Remoting 体系结构具有可扩展性,因此,您可以开发自定义接收并将其插入到处理管道中。要提供安全的通信,可以开发自定义接收,以便对发送到和来自远程对象的消息数据进行加密和解密。
这种方法的优点是您可以有选择地加密消息的一部分。这与平台级方法正好相反,平台级方法是对客户端和服务器之间发送的所有数据进行加密。
更多信息
有关 SSL 和 IPSec 的详细信息,请参见第 4 章安全通信。
要远程访问的对象必须在主机可执行文件中运行。主机侦听传入请求,并向对象发出调用。所选的主机类型会影响称为“通道”的消息传输机制。选择的通道类型影响身份验证、授权、安全通信和解决方案的性能。
HTTP 通道提供更好的安全性选项,但 TCP 通道提供更高的性能。
可以使用以下主要选项来驻留远程对象:
| • | 驻留在 ASP.NET 中 |
| • | 驻留在 Windows 服务中 |
| • | 驻留在控制台应用程序中 |
从安全的角度出发,要充分利用 ASP.NET 和 IIS 提供的安全基础结构,建议将远程对象驻留在 ASP.NET 中。这要求客户端通过 HTTP 通道与远程对象进行通信。驻留在 ASP.NET 中的远程对象可以使用 ASP.NET 和 IIS 身份验证、授权和安全通信功能。
如果主要关心性能(而不是安全性)问题,则考虑将远程对象驻留在 Windows 服务中。
在将远程对象驻留在 ASP.NET 中时:
| • | 使用 HTTP 协议可访问该对象。 |
| • | 它具有一个可通过 URL 访问的终结点。 |
| • | 它位于 Aspnet_wp.exe 辅助进程内的应用程序域中。 |
| • | 它继承 IIS 和 ASP.NET 提供的安全功能。 |
如果将远程对象驻留在 IIS 中,则您可以获得以下优势:
| • | 直接使用 IIS 和 ASP.NET 提供的身份验证、授权和安全通信功能。 |
| • | 可以使用 IIS 的审核功能。 |
| • | ASP.NET 辅助进程始终运行。 |
| • | 通过使用 Machine.config 中的 <processModel> 元素,对驻留的可执行文件进行更严格的控制。可以控制线程管理、容错性和内存管理等等。 |
| • | 可以在远程对象前面创建 Web 服务外层。 |
如果使用 ASP.NET 驻留远程对象,则应该了解它的以下缺点:
| • | 它要求使用的 HTTP 通道比 TCP 通道慢。 |
| • | ASP.NET 不加载用户配置文件。多种加密技术(包括 DPAPI)可能需要使用用户配置文件。 |
| • | 如果从 ASP.NET Web 应用程序中运行的代码访问对象,则可能需要使用基本身份验证。 |
如果将远程对象驻留在 Windows 服务中,则在服务进程内包含的应用程序域中激活远程对象。您不能使用 HTTP 通道,而必须使用 TCP 通道。TCP 通道支持以下安全功能:
| • | 身份验证功能 您必须提供自定义的身份验证解决方案。选项包括:
| ||||
| • | 授权功能
| ||||
| • | 安全通信功能 您有两个选项:
|
如果将远程对象驻留在 Windows 服务中,则可以获得以下优势:
| • | 对主机进程进行严格的激活控制。 |
| • | 继承 Windows 服务体系结构的优点。 |
| • | 无需在应用程序的中间层引入 IIS。 |
| • | 自动加载用户配置文件。 |
| • | 性能与客户端使用二进制编码数据通过 TCP 通道进行通信时相当。 |
如果使用 Windows 服务来驻留远程对象,则应该了解它的以下缺点:
| • | 必须提供自定义的身份验证和授权解决方案。 |
| • | 必须提供安全的通信解决方案。 |
| • | 必须提供审核解决方案。 |
如果将远程对象驻留在控制台应用程序中,则在控制台应用程序进程内包含的应用程序域中激活远程对象。您不能使用 HTTP 通道,而必须使用 TCP 通道。
建议在生产解决方案中不要使用这种方法。
虽然这种方法没有多少优点,但它在中间层的确不需要使用 IIS。不过,仅建议在开发和测试环境中使用这种方法,不要在生产环境中使用。
如果将远程对象驻留在自定义的可执行文件中,则应该了解它的以下缺点:
| • | 主机必须手动启动,并且在交互式登录会话中运行(不建议使用)。 |
| • | 没有容错功能。 |
| • | 必须提供自定义的身份验证和授权功能。 |
| • | 没有审核功能。 |
.NET 提供多种不同的方法使客户端能够与远程对象进行通信,其中包括使用 Web 服务。
如果需要异类系统之间具有互操作性,则最好选择使用开放式标准(例如 SOAP、XML 和 HTTP)的 Web 服务方法。另一方面,如果您正在创建服务器到服务器的 Intranet 解决方案,则远程处理组件可提供以下功能:
| • | 由于可以远程控制任何 .NET 类型(包括使用 Microsoft C#® 开发工具和 Microsoft Visual Basic® .NET 开发系统创建的自定义类型),因此各种对象保持不变。 |
| • | 可以按值和按引用来封送对象。 |
| • | 对象生命周期管理是基于租期进行的。 |
| • | 高性能,尤其是使用 TCP 通道和二进制格式化程序时。 |
| • | 它允许您使用网络负载平衡功能构建负载平衡的中间层。 |
表 11.2: 远程处理和 Web 服务的主要差别
| 远程处理 | Web 服务 |
全状态或无状态,基于租期的对象生命周期管理 | 所有方法调用都是无状态的 |
不需要 IIS | 必须在服务器上安装 IIS |
支持所有的托管类型 | 支持数量有限的数据类型。有关 |
可以按引用或按值传递对象 | 无法传递对象 |
包含可扩展的体系结构,并不 | 限于 HTTP 上的 XML |
可以将自定义的处理接收插入到 | 无法修改消息 |
SOAP 实现方式有所限制,并且 | SOAP 实现方式可以使用 RPC 或 |
紧密集成 | 松散集成 |
.NET Remoting 自身不提供安全模型。然而,通过将远程对象驻留在 ASP.NET 中并使用 HTTP 通道进行通信,远程对象可以使用 IIS 和 ASP.NET 提供的基本安全服务。比较而言,TCP 通道和自定义的主机可执行文件能够提供更高的性能,但这种组合不提供内置的安全功能。
| • | 若要对客户端进行身份验证,请使用 HTTP 通道,在 ASP.NET 中驻留对象,以及在 IIS 中禁用匿名访问。 |
| • | 如果您不担心客户端身份验证问题,请使用 TCP 通道,它可以提供更高的性能。 |
| • | 如果您使用 TCP 通道,请使用 IPSec 保护客户端和服务器之间的通信通道。使用 SSL 来保护 HTTP 通道。 |
| • | 如果您需要对远程资源进行受信任的调用,请将组件驻留在 Windows 服务中,而不是驻留在控制台应用程序中。 |
| • | IPrincipal 对象无法跨越 .NET Remoting 边界传递。可以考虑实现您自己的 IPrincipal 类(可以进行序列化)。如果您执行上述操作,则要注意,未经授权的客户端可以很容易地骗过 IPrincipal 对象并将它发送给远程对象。另外,如果您在远程处理过程中实现了您自己的 IPrincipal 类,则要慎用 IlogicalThreadAffinitive。 |
| • | 始终不要向 Internet 公开远程对象。在这种情况下,请使用 Web 服务。 |