新建一个类库名为“WcfSecurityExampleServiceLibrary”的类库项目,添加如代码清单11-10所示的契约,其中将示例契约命名为HelloService。
代码清单11-10 HelloService契约
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WcfSecurityExampleServiceLibrary
{
[ServiceContract]
public interface IHelloService
{
[OperationContract]
string GetHello();
}
}
代码清单11-11是HelloService契约的实现。
代码清单11-11 HelloService契约的实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
namespace WcfSecurityExampleServiceLibrary
{
public class HelloService : IHelloService
{
public string GetHello()
{
if (ServiceSecurityContext.Current != null)
{
if (!ServiceSecurityContext.Current.IsAnonymous)
{
return "Hello:" + ServiceSecurityContext.Current.PrimaryIdentity.Name + ";type="
+ ServiceSecurityContext.Current.PrimaryIdentity.AuthenticationType;
}
return "";
}
else
{
return "hello";
} }
}
}
这里采用控制台程序做自托管宿主,宿主代码如代码清单11-12所示。
代码清单11-12 宿主代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using WcfSecurityExampleServiceLibrary;
namespace SimpleHost
{
class Program
{
static void Main(string[] args)
{
ServiceHost hostForHello = new ServiceHost(typeof(HelloService));
hostForHello.Open();
try
{
while (true)
{
}
}
catch
{
hostForHello.Abort();
}
}
}
}
宿主配置文件如代码清单11-13所示。
代码清单11-13 宿主配置文件
<?xml version="1.0"?>
<configuration>
<system.serviceModel>
<services>
<service name="WcfSecurityExampleServiceLibrary.HelloService" behaviorConfiguration="mex">
<host>
<baseAddresses>
<add baseAddress="net.tcp://127.0.0.1:64567/"/>
</baseAddresses>
</host>
<endpoint address="net.tcp://127.0.0.1:64567/HelloService" binding="netTcpBinding"
bindingConfiguration="tcpWindowsSecurity" name="helloEndPoint"
contract="WcfSecurityExampleServiceLibrary.IHelloService"/>
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
</service>
</services>
<bindings>
<netTcpBinding>
<binding name="tcpWindowsSecurity">
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="mex">
<serviceMetadata />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
</configuration>
代码清单11-13所示的配置文件并没有对netTcpBinding做任何的安全配置,因此一切将采用默认设置。
客户端实现如代码清单11-14所示。
代码清单11-14 客户端实现
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using WcfSecurityExampleServiceLibrary;
namespace WcfSecurityExampleConsoleClient
{
class Program
{
static void Main(string[] args)
{
using (ChannelFactory<IHelloService> channelFactory = new ChannelFactory<IHelloService>("helloEndPoint"))
{
IHelloService helloService = channelFactory.CreateChannel();
using (helloService as IDisposable)
{
Console.WriteLine(helloService.GetHello());
}
}
Console.Read();
}
}
}
在代码请单11-14所示的实现中,首先根据配置文件创建服务管道,然后请求服务的GetHello方法,输出结果。客户端的配置文件,如代码清单11-15所示。
代码清单11-15 客户端配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="tcpWindowsSecurity">
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint name="helloEndPoint" address="net.tcp://127.0.0.1:64567/HelloService"
binding="netTcpBinding" bindingConfiguration="tcpWindowsSecurity"
contract="WcfSecurityExampleServiceLibrary.IHelloService" />
</client>
</system.serviceModel>
</configuration>
从代码清单11-15的配置文件中,可以看出客户的所有安全配置仍然采用默认的配置。
在运行代码之前,对代码清单11-12中用到的ServiceSecurityContext对象做简要的说明。
ServiceSecurityContext对象用于表示安全调用上下文。安全调用上下文保存在TLS中,沿着服务的调用链向下,每个对象的每个方法都可以访问当前的安全调用上下文。获得上下文的方法很简单,只需要访问ServiceSecurityContext对像的Current属性。这里需要注意ServiceSecurityContext对象的两个属性:PrimaryIdentity属性和WindowsIdentity属性。PrimaryIdentity属性包含了调用链最终客户端的身份,如果客户端未通过验证,该属性会被置一个空白身份,如果是Windows身份验证,那么会被赋予一个WindowsIdentity实例。WindowsIdentity属性只有在采用Windows身份验证时有效。
启动代码清单11-12所示的WCF宿主和代码清单11-14所示的客户端,运行结果如图11-6所示。
图11-6 HelloService测试结果
图11-6的运行结果可以证明,在默认情况下,netTcpBinding采用的是Transport安全模式,凭据类型为Windows。
继续修改客户端的配置为代码清单11-16所示的内容。然后启动TcpTrace来监听通信。
代码清单11-16 客户端的配置(为配合TcpTrace监听修改)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<behaviors >
<endpointBehaviors>
<behavior name="ForListen">
<clientVia viaUri="net.tcp://127.0.0.1:64590/HelloService"/>
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="tcpWindowsSecurity">
<!--<security mode="None"></security> -->
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint name="helloEndPoint" address="net.tcp://127.0.0.1:64567/HelloService"
binding="netTcpBinding" bindingConfiguration="tcpWindowsSecurity"
contract="WcfSecurityExampleServiceLibrary.IHelloService" behaviorConfiguration="ForListen" />
</client>
</system.serviceModel>
</configuration>
以上代码加粗的部分为新增的配置,配置了客户端的转向请求,转向的端口“64590”为TcpTrace的监听端口。再次运行程序,TcpTrace的监听监听结果如图11-7所示。
图11-7 监听Transport安全模式下的默认配置
从图11-7中可以看出,默认情况下无法看到结果的明文信息,说明对消息进行了加密。
为了更清晰的理解默认情况下NetTcpBinding的安全配置,这里给出两段代码和两个配置文件,它们实现的是相同的效果。
初始化NetTcpBinding类实例1:
NetTcpBinding netTcpBingding = new NetTcpBinding();
初始化NetTcpBinding类实例2:
NetTcpBinding netTcpBingding = new NetTcpBinding();
netTcpBingding.Security.Mode = SecurityMode.Transport;
netTcpBingding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
netTcpBingding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
NetTcpBinding默认安全配置文件1:
<netTcpBinding>
<binding name="tcpWindowsSecurity">
<security>
</security>
</binding>
</netTcpBinding>
NetTcpBinding默认安全配置文件2:
<netTcpBinding>
<binding name="tcpWindowsSecurity">
<security mode="Transport" >
<transport
protectionLevel="EncryptAndSign"
clientCredentialType="Windows">
</transport>
</security>
</binding>
</netTcpBinding>
---------------------------------注:本文部分内容改编自《.NET 安全揭秘》