WCF安全:通過 擴展實現用戶名密碼認證
在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、運行結果圖;
服務端;

客戶端;

浙公網安備 33010602011771號