了解保护级别

ProtectionLevel 属性位于许多不同的类上,例如 ServiceContractAttribute 类和 OperationContractAttribute 类。 该属性控制消息的一部分(或整个)如何受到保护。 本主题介绍 Windows Communication Foundation (WCF) 功能及其工作原理。

有关设置保护级别的说明,请参阅 如何:设置 ProtectionLevel 属性

注释

保护级别只能在代码中设置,而不能在配置中设置。

基础

若要了解保护级别功能,以下基本语句适用:

  • 消息的任何部分都存在三个基本级别的保护。 属性(无论它出现在哪里)都被设置为ProtectionLevel的枚举值之一。 按保护等级从低到高,它们包括:

    • None

    • Sign。 受保护的部件经过数字签名。 这可确保检测任何篡改受保护消息部件。

    • EncryptAndSign。 消息部件经过加密,以确保在签名之前保持机密性。

  • 只能为具有此功能 的应用程序数据 设置保护要求。 例如,WS-Addressing 标头是基础结构数据,因此不受基础结构 ProtectionLevel数据的影响。

  • 当安全模式设置为 Transport“安全模式”时,整个消息将受到传输机制的保护。 因此,为消息的不同部分设置单独的保护级别不起作用。

  • ProtectionLevel是供开发人员设置绑定必须遵从的最低级别的方法。 部署服务时,配置中指定的实际绑定可能或可能不支持最低级别。 例如,默认情况下, BasicHttpBinding 该类不提供安全性(尽管可以启用)。 因此,将它与具有任何非 None 设置的协定一起使用将导致引发异常。

  • 如果服务要求所有消息的最小ProtectionLevelSign,那么由非 WCF 技术创建的客户端可以加密和签名所有消息(从而达到超过所需的标准)。 在这种情况下,WCF 不会抛出异常,因为客户端已经做了超出最低要求的工作。 但是,请注意,WCF 应用程序(服务或客户端)在可能的情况下不会过度保护消息部分,但会符合最低级别。 另请注意,当用作 Transport 安全模式时,传输可能会过度保护消息流,因为它本质上无法在更精细的级别进行保护。

  • 如果将ProtectionLevel显式设置为SignEncryptAndSign,则必须使用启用安全性的绑定,否则将引发异常。

  • 如果选择一个绑定来启用安全性,并且未在 ProtectionLevel 协定上的任何位置设置属性,则会加密和签名所有应用程序数据。

  • 如果选择未启用安全性的绑定(例如, BasicHttpBinding 类默认禁用了安全性),并且 ProtectionLevel 未显式设置,则不会保护任何应用程序数据。

  • 如果使用在传输级别应用安全性的绑定,则所有应用程序数据都将根据传输的功能进行保护。

  • 如果使用在消息级别应用安全性的绑定,则根据协定上设置的保护级别来保护应用程序数据。 如果未指定保护级别,则消息中的所有应用程序数据都将经过加密和签名。

  • 可以在不同范围级别设置 ProtectionLevel。 有一个与范围关联的层次结构,下一部分对此进行了说明。

范围

设置最顶层 API 上的 ProtectionLevel 可以为它下面所有级别设置级别。 如果设置为 ProtectionLevel 较低级别的不同值,则层次结构中低于该级别的所有 API 现在都将重置为新级别(但高于该级别的 API 仍会受到最顶层的影响)。 层次结构如下所示。 同一级别的属性是对等的。

编程保护级别

若要对层次结构中的任何点进行编程 ProtectionLevel ,只需在应用属性时将该属性设置为适当的值。 有关示例,请参阅 “如何:设置 ProtectionLevel 属性”。

注释

要设置错误和消息协定的属性,需要了解这些功能的工作原理。 有关详细信息,请参阅 如何:设置 ProtectionLevel 属性 和使用 消息协定

WS-Addressing 依赖性

在大多数情况下,使用 ServiceModel 元数据实用工具工具(Svcutil.exe) 生成客户端可确保客户端和服务协定完全相同。 但是,看起来相同的协定却可能导致客户端引发异常。 每当绑定不支持 WS-Addressing 规范并且协定上指定了多个级别的保护时,就会出现这种情况。 例如,该 BasicHttpBinding 类不支持规范,或者创建不支持 WS 寻址的自定义绑定。 该功能 ProtectionLevel 依赖于 WS-Addressing 规范在单个协定上启用不同的保护级别。 如果绑定不支持 WS-Addressing 规范,则所有级别都将设置为相同的保护级别。 合同上所有范围的有效保护级别将设置为协定中使用的最强保护级别。

这可能会引起初看上去难以调试的问题。 可以创建包含多个服务方法的客户端协定(接口)。 也就是说,同一接口用于创建与许多服务通信的客户端,而单个接口包含所有服务的方法。 开发人员必须注意这一罕见的情况,只调用适用于每个特定服务的方法。 如果绑定是 BasicHttpBinding 类,则不支持多个保护级别。 但是,回复客户端的服务可能会响应保护级别低于要求的客户端。 在这种情况下,客户端将引发异常,因为它需要更高的保护级别。

代码示例演示了此问题。 以下示例演示服务和客户端协定。 假定绑定是 <basicHttpBinding> 元素。 因此,合同上的所有操作都具有相同的保护级别。 此统一保护级别被确定为所有操作中的最大保护级别。

服务协定为:

[ServiceContract()]
public interface IPurchaseOrder
{
    [OperationContract(ProtectionLevel = ProtectionLevel.Sign)]
    int Price();
}
<ServiceContract()> _
Public Interface IPurchaseOrder
    <OperationContract(ProtectionLevel:=ProtectionLevel.Sign)> _
    Function Price() As Integer
End Interface

以下代码显示了客户端协定接口。 请注意,它包括一个旨在与其他服务一起使用的 Tax 方法:

[ServiceContract()]
public interface IPurchaseOrder
{
    [OperationContract()]
    int Tax();

    [OperationContract(ProtectionLevel = ProtectionLevel.Sign)]
    int Price();
}
<ServiceContract()> _
Public Interface IPurchaseOrder
    <OperationContract()> _
    Function Tax() As Integer

    <OperationContract(ProtectionLevel:=ProtectionLevel.Sign)> _
    Function Price() As Integer
End Interface

当客户端调用 Price 该方法时,它会在从服务收到回复时引发异常。 之所以发生这种情况,是因为客户端未在 ProtectionLevel 上指定 ServiceContractAttribute,因此客户端对所有方法(包括 EncryptAndSign 方法)使用默认值(Price)。 但是,服务使用 Sign 级别返回值,因为服务协定定义了其保护级别设置为 Sign的单个方法。 在这种情况下,客户端在验证来自服务的响应时将引发错误。

另请参阅