WCF 學習總結5 -- 消息攔截實現用戶名驗證(轉)

WCF創建在基於消息的通訊這一律念基礎上。經過方法調用(Method Call)形式體現的服務訪問須要轉化成具體的消息,並經過相應的編碼(Encoding)才能經過傳輸通道發送到服務端;服務操做執行的結果也只能以消息的形式才能被正常地返回到客戶端。因此,消息在整個WCF體系結構中處於一個核心的地位,WCF能夠當作是一個消息處理的管道,以下圖所示: 
11_133734_wcf_architecture  
WCF的一個操做(以及操做的參數)被序列化爲Soap協議所支持的消息(XML結構),通過服務運行層,交給Binding中所定義的消息傳遞層,消息傳遞層由通道(Channel)組成。通道是以某種方式對消息進行處理(例如經過對消息進行身份驗證)的組件,通道對消息和消息頭進行操做,而服務運行層主要針對消息正文內容進行處理。 
IC5864  

方法一. 經過OperationContext直接添加/訪問MessageHeader信息 
使用OperationContext咱們能夠:訪問當前操做執行環境。 特別是,操做上下文用於訪問雙工服務中的回調通道、存儲整個操做部分的額外狀態數據、訪問傳入消息頭和屬性以及添加傳出消息頭和屬性。下面用代碼演示下如何在MessageHeader中添加額外的信息,進行用戶驗證。 
1. 服務契約 ide

[c-sharp]  view plain copy
 
  1. using System;  
  2. using System.Runtime.Serialization;  
  3. using System.ServiceModel;  
  4. namespace WcfSvcLib  
  5. {  
  6.     [ServiceContract(Namespace="http://blog.csdn.net/fangxinggood")]  
  7.     public interface IService1  
  8.     {  
  9.         [OperationContract]  
  10.         string GetData(int value);  
  11.     }  
  12. }  


2. 服務實現 編碼

[c-sharp]  view plain copy
 
  1. using System;  
  2. using System.Runtime.Serialization;  
  3. using System.ServiceModel;  
  4. namespace WcfSvcLib  
  5. {  
  6.     public class Service1 : IService1  
  7.     {  
  8.         public string GetData(int value)  
  9.         {  
  10.             Console.WriteLine(OperationContext.Current.RequestContext.RequestMessage);  
  11.             // 注意namespace必須和ServiceContract中定義的namespace保持一致,默認是:http://tempuri.org  
  12.             var ns = "http://blog.csdn.net/fangxinggood";  
  13.             var user = GetHeaderValue("user", ns);  
  14.             var pwd = GetHeaderValue("pwd", ns);  
  15.             // 驗證失敗會拋出Invalid User的異常。  
  16.             if (user != "fangxing" || pwd != "password")  
  17.                 throw new FaultException("Invalid User!");  
  18.             return string.Format("You entered: {0}", value);  
  19.         }  
  20.         private string GetHeaderValue(string name, string ns = "http://tempuri.org")  
  21.         {  
  22.             var headers = OperationContext.Current.IncomingMessageHeaders;  
  23.             var index = headers.FindHeader(name, ns);  
  24.             if (index > -1)  
  25.                 return headers.GetHeader<string>(index);  
  26.             else  
  27.                 return null;  
  28.         }  
  29.     }  
  30. }  


3. 客戶端實現 spa

[c-sharp]  view plain copy
 
  1. using System;  
  2. using System.ServiceModel;  
  3. using System.ServiceModel.Channels;  
  4. namespace WcfClient  
  5. {  
  6.     class Program  
  7.     {  
  8.         static void Main(string[] args)  
  9.         {  
  10.             var client = new WcfSvc.Service1Client();  
  11.             using (var scope = new OperationContextScope(client.InnerChannel))  
  12.             {  
  13.                 // 注意namespace必須和ServiceContract中定義的namespace保持一致,默認是:http://tempuri.org  
  14.                 var myNamespace = "http://blog.csdn.net/fangxinggood";  
  15.                 // 注意Header的名字中不能出現空格,由於要做爲Xml節點名。  
  16.                 var user = MessageHeader.CreateHeader("user", myNamespace, "fangxing");  
  17.                 var pwd = MessageHeader.CreateHeader("pwd", myNamespace, "password");  
  18.                 OperationContext.Current.OutgoingMessageHeaders.Add(user);  
  19.                 OperationContext.Current.OutgoingMessageHeaders.Add(pwd);  
  20.                 var result = client.GetData(100);  
  21.                 Console.WriteLine(result);  
  22.                 Console.Read();  
  23.             }  
  24.         }  
  25.     }  
  26. }  


運行一下,在服務端經過 Console.WriteLine(OperationContext.Current.RequestContext.RequestMessage); 輸出了請求的Message。經過輸出的信息,咱們能夠看到Header裏添加的信息: 
simpleSoapHeaderMessage 

經過上面的代碼,咱們能夠完成相似WebService的SoapHeader驗證。可是這樣須要咱們每一個契約都作相似的添加、驗證,這樣豈不是很繁瑣。下面看方法二,經過消息檢查器完成統一的用戶驗證。 

方法二. 消息檢查器方式添加/訪問MessageHeader信息 
客戶端經過實現IClientMessageInspector接口,服務端經過實現IDispatchMessageInspector接口,來攔截消息。這種方式是經過擴展Behavior來加入攔截的,因此還須要分別實現IEndpointBehavior(客戶端)和IServiceBehavior(服務端)接口,並經過配置將消息檢查器加入。 
工程結構: 
 messageInterpectorProjects 實現說明: 
【客戶端】
 
1. ClientInterpector 實現: .net

[c-sharp]  view plain copy
 
  1. using System;  
  2. using System.ServiceModel.Dispatcher;  
  3. using System.ServiceModel.Configuration;  
  4. using System.ServiceModel.Channels;  
  5. using System.ServiceModel;  
  6. namespace WcfClientInterpector  
  7. {  
  8.     public class ClientInterpector : IClientMessageInspector  
  9.     {  
  10.         public void AfterReceiveReply(ref Message reply, object correlationState)  
  11.         {  
  12.         }  
  13.         public object BeforeSendRequest(ref Message request, IClientChannel channel)  
  14.         {  
  15.             var userNameHeader = MessageHeader.CreateHeader("OperationUserName""http://tempuri.org""fangxing"false"");  
  16.             var pwdNameHeader = MessageHeader.CreateHeader("OperationPwd""http://tempuri.org""password"false"");  
  17.             request.Headers.Add(userNameHeader);  
  18.             request.Headers.Add(pwdNameHeader);  
  19.             Console.WriteLine(request);  
  20.             return null;  
  21.         }  
  22.     }  
  23. }  


2. MyClientBehavior 實現: (實現擴展endpointBehavior元素) orm

[c-sharp]  view plain copy
 
  1. using System;  
  2. using System.ServiceModel.Configuration;  
  3. using System.ServiceModel.Description;  
  4. using System.ServiceModel.Dispatcher;  
  5. using System.ServiceModel;  
  6. namespace WcfClientInterpector  
  7. {  
  8.     public class MyClientBehavior : BehaviorExtensionElement, IEndpointBehavior  
  9.     {  
  10.         public override Type BehaviorType  
  11.         {  
  12.             get { return typeof(MyClientBehavior); }  
  13.         }  
  14.         protected override object CreateBehavior()  
  15.         {  
  16.             return new MyClientBehavior();  
  17.         }  
  18.         #region IEndpointBehavior Members  
  19.         public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)  
  20.         {  
  21.         }  
  22.         public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)  
  23.         {  
  24.             clientRuntime.MessageInspectors.Add(new ClientInterpector());  
  25.         }  
  26.         public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)  
  27.         {  
  28.         }  
  29.         public void Validate(ServiceEndpoint endpoint)  
  30.         {  
  31.         }  
  32.         #endregion  
  33.     }  
  34. }  


3. 配置 
修改客戶端配置文件,步驟以下: 
(1) 在Advanced>Extensions>behavior element extensions中加入自定義的ClientInterpector。 
clientInterpectorConfig1  
(2) 在Advanced>Endpoint Behaviors中定義一個Behavior,添加上面配置過的extension 
clientInterpectorConfig2  
(3) 修改Client>Endpoints下的Endpoint的Behavior Config指向(2)配置的Behavior。 
clientInterpectorConfig3 

【服務端】 
1. ServiceInterpector 實現: server

[c-sharp]  view plain copy
 
  1. using System;  
  2. using System.ServiceModel.Dispatcher;  
  3. using System.ServiceModel;  
  4. namespace WcfServiceInterpector  
  5. {  
  6.     public class ServiceInterpector : IDispatchMessageInspector  
  7.     {  
  8.         #region IDispatchMessageInspector Members  
  9.         public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)  
  10.         {  
  11.             Console.WriteLine(request);  
  12.             var user = GetHeaderValue("OperationUserName");  
  13.             var pwd = GetHeaderValue("OperationPwd");  
  14.             if (user != "fangxing" || pwd != "password")  
  15.                 throw new FaultException("Invalid User!");  
  16.             return null;  
  17.         }  
  18.         public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)  
  19.         {  
  20.         }  
  21.         private string GetHeaderValue(string name, string ns = "http://tempuri.org")  
  22.         {  
  23.             var headers = OperationContext.Current.IncomingMessageHeaders;  
  24.             var index = headers.FindHeader(name, ns);  
  25.             if (index > -1)  
  26.                 return headers.GetHeader<string>(index);  
  27.             else  
  28.                 return null;  
  29.         }  
  30.         #endregion  
  31.     }  
  32. }  


2. MyServiceBehavior 實現:(實現擴展serviceBehavior元素) blog

[c-sharp]  view plain copy
 
  1. using System;  
  2. using System.ServiceModel.Configuration;  
  3. using System.ServiceModel.Description;  
  4. using System.ServiceModel.Dispatcher;  
  5. namespace WcfServiceInterpector  
  6. {  
  7.     public class MyServiceBehavior : BehaviorExtensionElement, IServiceBehavior  
  8.     {  
  9.         public override Type BehaviorType  
  10.         {  
  11.             get { return typeof(MyServiceBehavior); }  
  12.         }  
  13.         protected override object CreateBehavior()  
  14.         {  
  15.             return new MyServiceBehavior();  
  16.         }  
  17.         #region IServiceBehavior Members  
  18.         public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)  
  19.         {  
  20.         }  
  21.         public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)  
  22.         {  
  23.             foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers)  
  24.             {  
  25.                 foreach (EndpointDispatcher epDisp in chDisp.Endpoints)  
  26.                 {  
  27.                     epDisp.DispatchRuntime.MessageInspectors.Add(new ServiceInterpector());  
  28.                 }  
  29.             }  
  30.         }  
  31.         public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)  
  32.         {  
  33.         }  
  34.         #endregion  
  35.     }  
  36. }  


3. 配置 
修改服務端配置文件,步驟以下: 
(1) 在Advanced>Extensions>behavior element extensions中加入自定義的ServiceInterpector。 
serverInterpectorConfig1  
(2) 在Advanced>Service Behaviors中定義一個Behavior,添加上面配置過的extension 
serverInterpectorConfig2  
(3) 修改Services下的服務節點的Behavior Config指向(2)配置的Behavior。 
serverInterpectorConfig3  接口

相關文章
相關標籤/搜索