在webSservice时代,可以通过SOAPHEADER的方式很容易将用户名、密码附加到SOAP header消息头上,用户客户端对调用客户端身份的验证。在WCF 时代,也可以通过OperationContext.Current.IncomingMessageHeaders的方式将用户名、密码附加到SOAP消息中。但是这种方式实现起来有个缺点;那就是所有调用客户端都需要这样做才能将我们需要通过认证的帐号、密码附加到SOAP消息上。实际上,也可以通过WCF扩展的方式,在客户端自动将用户名、密码附加到SOAP消息中。这即是本文主题。
1、客户端消息自动附加用户名、密码
实现IClientMessageInspector接口,用于附加调用者的账号、密码信息
public class ClientMessageInspector : IClientMessageInspector { public void AfterReceiveReply(ref Message reply, object correlationState) { } public object BeforeSendRequest(ref Message request, IClientChannel channel) { MessageHeader userNameHeader = MessageHeader.CreateHeader("OperationUserName", "http://tempuri.org", "account", false, ""); MessageHeader pwdNameHeader = MessageHeader.CreateHeader("OperationPwd", "http://tempuri.org", "password", false, ""); request.Headers.Add(userNameHeader); request.Headers.Add(pwdNameHeader); Console.WriteLine(request); return null; } }
2、实现IEndpointBehavior 接口。用户将客户端的消息检查器添加到clientruntime的消息检查器集合中.注意,以下代码中为了简单将服务端分发消息检查器也添加到服务端终结点分发器(EndpointDispatcher)的分发运行时的消息检查器中。
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { clientRuntime.MessageInspectors.Add(new ClientMessageInspector());//客户端使用 } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MessageDispatcher());//服务端使用 } public void Validate(ServiceEndpoint endpoint) { }
3、实现抽象类BehaviorextensionElement.用于在配置文件中配置对WCF服务行为的扩展
internal class MessageBindingElement : BehaviorExtensionElement { public override Type BehaviorType { get { return typeof (MessageEndpointBehavior); } } protected override object CreateBehavior() { return new MessageEndpointBehavior(); } }
4、服务端验证客户端的账号、密码
实现IDispatcherMessageInspector
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext) { Console.WriteLine(request); string userName = GetHeaderValue("OperationUserName"); string pwd = GetHeaderValue("OperationPwd"); if ("account" == userName && "password" == pwd) { return null; } throw new Exception("用户名、密码错误"); } public void BeforeSendReply(ref Message reply, object correlationState) { } string GetHeaderValue(string key) { int index = OperationContext.Current.IncomingMessageHeaders.FindHeader(key, "http://tempuri.org"); if (index >= 0) { return OperationContext.Current.IncomingMessageHeaders.GetHeader<string>(index).ToString(); } return null; }
5、服务端配置;
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <extensions> <behaviorExtensions> <add name="messageInterptor" type="MessageInterceptor.MessageBindingElement,MessageInterceptor"/> </behaviorExtensions> </extensions> <behaviors> <endpointBehaviors> <behavior name="messageBehavior"> <messageInterptor /> </behavior> </endpointBehaviors> </behaviors> <services> <service name="Services.CalculatorService"> <endpoint address="net.tcp://127.0.0.1:8081/CalculateService" binding="netTcpBinding" contract="Contracts.ICalculator" behaviorConfiguration="messageBehavior"></endpoint> </service> </services> </system.serviceModel> </configuration>
6、客户端配置;
<?xml version="1.0"?> <configuration> <system.serviceModel> <client> <endpoint name="calculatorService" address="net.tcp://127.0.0.1:8081/CalculateService" binding="netTcpBinding" contract="Contracts.ICalculator" behaviorConfiguration="messageBehavior"> </endpoint> </client> <extensions> <behaviorExtensions> <add name="messageInterptor" type="MessageInterceptor.MessageBindingElement,MessageInterceptor"/> </behaviorExtensions> </extensions> <behaviors> <endpointBehaviors> <behavior name="messageBehavior"> <messageInterptor /> </behavior> </endpointBehaviors> </behaviors> </system.serviceModel> </configuration>
7、运行结果图;
服务端;
客户端;