日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

传说中的WCF:消息拦截与篡改

發布時間:2025/3/8 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 传说中的WCF:消息拦截与篡改 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我們知道,在WCF中,客戶端對服務操作方法的每一次調用,都可以被看作是一條消息,而且,可能我們還會有一個疑問:如何知道客戶端與服務器通訊過程中,期間發送和接收的SOAP是什么樣子。當然,也有人是通過借助其他工具來抓取數據包來查看。那,有沒有辦法讓程序自己輸出相應的SOAP信息呢?

當然有,這就是我們本文要說的,對消息的攔截與篡改,呵,我用了一個不太好聽動詞——篡改。

由于WCF的模型相對復雜,對于如何攔截和修改消息會讓許多剛接觸的朋友有點抓狂。是的,雖然MSDN文檔都有詳細的說明,但估計你也和我有相同的感覺,看了MSDN的說明后依然一頭霧水。確實如此,畢竟WCF不像窗口和控件那樣可以看得見,理解起來比較直觀,相反的,這些東西會相對抽象。

說到消息攔截,這個你肯定可以理解,如果你不懂,你可以想一想電話竊聽程序,我在你的手機上植入一種木馬,可以截取你和MM的通話內容,其實這就是消息攔截。

WCF相關的API比較難尋找,我當初也找了N久,現在,我直接把思路和方法告訴各位,也免得大家太辛苦。

要對SOAP消息進行攔截和修改,我們需要實現兩個接口,它們都位于System.ServiceModel.Dispatcher (程序集System.ServiceModel)。下面分別價紹。

接口一:IClientMessageInspector

從名字中我們可以猜測,它是用來攔截客戶消息的,而看看它的方法,你就更加肯定當初的猜測了。

  • BeforeSendRequest:向服務器發送請求前攔截或修改消息(事前控制)
  • AfterReceiveReply:接收到服務器的回復消息后,在調用返回之前攔截或修改消息(事后諸葛亮)

接口二:IDispatchMessageInspector

剛才那個接口是針對客戶端的,而這個是針對服務器端的。

  • AfterReceiveRequest:接收客戶端請求后,在進入操作處理代碼之前攔截或修改消息(欺上)
  • BeforeSendReply:服務器向客戶端發送回復消息之前攔截和修改消息(瞞下)。

雖然實現了這兩個接口,但你會有新的疑問,怎么用?把它們放到哪兒才能攔截消息?因此,下一步就是要實現IEndpointBehavior按口(System.ServiceModel.Description命名空間,程序集System.ServiceModel),它有四個方法,而我們只需要處理兩個就夠了。

下面是MSDN的翻譯版本說明:

    • 使用?ApplyClientBehavior?方法可以在客戶端應用程序中修改、檢查或插入對終結點中的擴展。

    • 使用?ApplyDispatchBehavior?方法可以在服務應用程序中修改、檢查或插入對終結點范圍執行的擴展。

      我想不用額外解釋了,說白了就是一個在客戶攔截和修改消息,另一個在服務器端攔截和修改消息。

      在實現這兩個方法時,和前面我們實現的IClientMessageInspector和IDispatchMessageInspector聯系起來就OK了。

      做完了IEndpointBehavior的事情后,把它插入到服務終結點中就行了,無論是服務器端還是客戶端,這一步都必須的,因為我們實現的攔截器是包括兩個端的,因此,較好的做法是把這些類寫到一個獨立的類庫(dll)中,這樣一來,服務器端和客戶端都可以引用它。詳見后面的示例。

      ?

      理論課上完了,下面開始實驗課,按照前面的指導思想,我們先要寫一個類庫。

      新建一個類庫應用,然后添加System.ServiceModel程序集的引用,這個不用我教你了,你懂的。

      [csharp]?view plaincopy print?
    • using?System;??
    • using?System.Collections.Generic;??
    • using?System.Linq;??
    • using?System.Text;??
    • using?System.Threading.Tasks;??
    • ??
    • using?System.ServiceModel;??
    • using?System.ServiceModel.Dispatcher;??
    • using?System.ServiceModel.Description;??
    • using?System.ServiceModel.Channels;??
    • ??
    • namespace?MyLib??
    • {??
    • ????///?<summary>??
    • ????///??消息攔截器??
    • ????///?</summary>??
    • ????public?class?MyMessageInspector:IClientMessageInspector,IDispatchMessageInspector??
    • ????{??
    • ????????void?IClientMessageInspector.AfterReceiveReply(ref?Message?reply,?object?correlationState)??
    • ????????{??
    • ????????????Console.WriteLine("客戶端接收到的回復:\n{0}",?reply.ToString());??
    • ????????}??
    • ??
    • ????????object?IClientMessageInspector.BeforeSendRequest(ref?Message?request,?IClientChannel?channel)??
    • ????????{??
    • ????????????Console.WriteLine("客戶端發送請求前的SOAP消息:\n{0}",?request.ToString());??
    • ????????????return?null;??
    • ????????}??
    • ??
    • ????????object?IDispatchMessageInspector.AfterReceiveRequest(ref?Message?request,?IClientChannel?channel,?InstanceContext?instanceContext)??
    • ????????{??
    • ????????????Console.WriteLine("服務器端:接收到的請求:\n{0}",?request.ToString());??
    • ????????????return?null;??
    • ????????}??
    • ??
    • ????????void?IDispatchMessageInspector.BeforeSendReply(ref?Message?reply,?object?correlationState)??
    • ????????{??
    • ????????????Console.WriteLine("服務器即將作出以下回復:\n{0}",?reply.ToString());??
    • ????????}??
    • ????}??
    • ??
    • ????///?<summary>??
    • ????///?插入到終結點的Behavior??
    • ????///?</summary>??
    • ????public?class?MyEndPointBehavior?:?IEndpointBehavior??
    • ????{??
    • ????????public?void?AddBindingParameters(ServiceEndpoint?endpoint,?BindingParameterCollection?bindingParameters)??
    • ????????{??
    • ????????????//?不需要??
    • ????????????return;??
    • ????????}??
    • ??
    • ????????public?void?ApplyClientBehavior(ServiceEndpoint?endpoint,?ClientRuntime?clientRuntime)??
    • ????????{??
    • ????????????//?植入“偷聽器”客戶端??
    • ????????????clientRuntime.ClientMessageInspectors.Add(new?MyMessageInspector());??
    • ????????}??
    • ??
    • ????????public?void?ApplyDispatchBehavior(ServiceEndpoint?endpoint,?EndpointDispatcher?endpointDispatcher)??
    • ????????{??
    • ????????????//?植入“偷聽器”?服務器端??
    • ????????????endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new?MyMessageInspector());??
    • ????????}??
    • ??
    • ????????public?void?Validate(ServiceEndpoint?endpoint)??
    • ????????{??
    • ????????????//?不需要??
    • ????????????return;??
    • ????????}??
    • ????}??
    • ??
    • }??

    • 這一步,我們先建立服務器端。

      記得要引用我們剛才寫的類庫。

      [csharp]?view plaincopy print?
    • using?System;??
    • using?System.Collections.Generic;??
    • using?System.Linq;??
    • using?System.Text;??
    • using?System.Threading.Tasks;??
    • ??
    • using?System.Runtime;??
    • using?System.Runtime.Serialization;??
    • using?System.ServiceModel;??
    • using?System.ServiceModel.Description;??
    • ??
    • namespace?WCFServer??
    • {??
    • ????class?Program??
    • ????{??
    • ????????static?void?Main(string[]?args)??
    • ????????{??
    • ????????????//?服務器基址??
    • ????????????Uri?baseAddress?=?new?Uri("http://localhost:1378/services");??
    • ????????????//?聲明服務器主機??
    • ????????????using?(ServiceHost?host?=?new?ServiceHost(typeof(MyService),?baseAddress))??
    • ????????????{??
    • ????????????????//?添加綁定和終結點??
    • ????????????????WSHttpBinding?binding?=?new?WSHttpBinding();??
    • ????????????????host.AddServiceEndpoint(typeof(IService),?binding,?"/test");??
    • ????????????????//?添加服務描述??
    • ????????????????host.Description.Behaviors.Add(new?ServiceMetadataBehavior?{?HttpGetEnabled?=?true?});??
    • ????????????????//?把自定義的IEndPointBehavior插入到終結點中??
    • ????????????????foreach?(var?endpont?in?host.Description.Endpoints)??
    • ????????????????{??
    • ????????????????????endpont.EndpointBehaviors.Add(new?MyLib.MyEndPointBehavior());??
    • ????????????????}??
    • ????????????????try??
    • ????????????????{??
    • ????????????????????//?打開服務??
    • ????????????????????host.Open();??
    • ????????????????????Console.WriteLine("服務已啟動。");??
    • ????????????????}??
    • ????????????????catch?(Exception?ex)??
    • ????????????????{??
    • ????????????????????Console.WriteLine(ex.Message);??
    • ????????????????}??
    • ????????????????Console.ReadKey();??
    • ????????????}??
    • ????????}??
    • ????}??
    • ??
    • ????[ServiceContract(Namespace?=?"MyNamespace")]??
    • ????public?interface?IService??
    • ????{??
    • ????????[OperationContract]??
    • ????????int?AddInt(int?a,?int?b);??
    • ????????[OperationContract]??
    • ????????Student?GetStudent();??
    • ????????[OperationContract]??
    • ????????CalResultResponse?ComputingNumbers(CalcultRequest?inMsg);??
    • ????}??
    • ??
    • ????[ServiceBehavior(IncludeExceptionDetailInFaults?=?true)]??
    • ????public?class?MyService?:?IService??
    • ????{??
    • ????????public?int?AddInt(int?a,?int?b)??
    • ????????{??
    • ????????????return?a?+?b;??
    • ????????}??
    • ??
    • ????????public?Student?GetStudent()??
    • ????????{??
    • ????????????Student?stu?=?new?Student();??
    • ????????????stu.StudentName?=?"小明";??
    • ????????????stu.StudentAge?=?22;??
    • ????????????return?stu;??
    • ????????}??
    • ??
    • ????????public?CalResultResponse?ComputingNumbers(CalcultRequest?inMsg)??
    • ????????{??
    • ????????????CalResultResponse?rmsg?=?new?CalResultResponse();??
    • ????????????switch?(inMsg.Operation)??
    • ????????????{??
    • ????????????????case?"加":??
    • ????????????????????rmsg.ComputedResult?=?inMsg.NumberA?+?inMsg.NumberB;??
    • ????????????????????break;??
    • ????????????????case?"減":??
    • ????????????????????rmsg.ComputedResult?=?inMsg.NumberA?-?inMsg.NumberB;??
    • ????????????????????break;??
    • ????????????????case?"乘":??
    • ????????????????????rmsg.ComputedResult?=?inMsg.NumberA?*?inMsg.NumberB;??
    • ????????????????????break;??
    • ????????????????case?"除":??
    • ????????????????????rmsg.ComputedResult?=?inMsg.NumberA?/?inMsg.NumberB;??
    • ????????????????????break;??
    • ????????????????default:??
    • ????????????????????throw?new?ArgumentException("運算操作只允許加、減、乘、除。");??
    • ????????????????????break;??
    • ????????????}??
    • ????????????return?rmsg;??
    • ????????}??
    • ????}??
    • ??
    • ????[DataContract]??
    • ????public?class?Student??
    • ????{??
    • ????????[DataMember]??
    • ????????public?string?StudentName;??
    • ????????[DataMember]??
    • ????????public?int?StudentAge;??
    • ????}??
    • ??
    • ????[MessageContract]??
    • ????public?class?CalcultRequest??
    • ????{??
    • ????????[MessageHeader]??
    • ????????public?string?Operation;??
    • ????????[MessageBodyMember]??
    • ????????public?int?NumberA;??
    • ????????[MessageBodyMember]??
    • ????????public?int?NumberB;??
    • ????}??
    • ??
    • ????[MessageContract]??
    • ????public?class?CalResultResponse??
    • ????{??
    • ????????[MessageBodyMember]??
    • ????????public?int?ComputedResult;??
    • ????}??
    • }??

    • 接下來,實現客戶端。

      a、引用剛才寫的類庫MyLib;

      b、引用WCF服務。

      [csharp]?view plaincopy print?
    • using?System;??
    • using?System.Collections.Generic;??
    • using?System.Linq;??
    • using?System.Text;??
    • using?System.Threading.Tasks;??
    • ??
    • namespace?WCFClient??
    • {??
    • ????class?Program??
    • ????{??
    • ????????static?void?Main(string[]?args)??
    • ????????{??
    • ????????????WS.ServiceClient?client?=?new?WS.ServiceClient();??
    • ????????????//?記得在客戶端也要插入IEndPointBehavior??
    • ????????????client.Endpoint.EndpointBehaviors.Add(new?MyLib.MyEndPointBehavior());??
    • ????????????try??
    • ????????????{??
    • ????????????????//?1、調用帶元數據參數和返回值的操作??
    • ????????????????Console.WriteLine("\n20和35相加的結果是:{0}",?client.AddInt(20,?35));??
    • ????????????????//?2、調用帶有數據協定的操作??
    • ????????????????WS.Student?student?=?client.GetStudent();??
    • ????????????????Console.WriteLine("\n學生信息---------------------------");??
    • ????????????????Console.WriteLine("姓名:{0}\n年齡:{1}",?student.StudentName,?student.StudentAge);??
    • ????????????????//?3、調用帶消息協定的操作??
    • ????????????????Console.WriteLine("\n15乘以70的結果是:{0}",?client.ComputingNumbers("乘",?15,?70));??
    • ????????????}??
    • ????????????catch?(Exception?ex)??
    • ????????????{??
    • ????????????????Console.WriteLine("異常:{0}",?ex.Message);??
    • ????????????}??
    • ??
    • ????????????client.Close();??
    • ????????????Console.ReadKey();??
    • ????????}??
    • ????}??
    • }??

    • 現在你可以運行程序來觀察了。

      ?


      ?

      知道了如何攔截消息,那么修改消息就不難了。

      現在我們把前面寫的類庫MyLib。

      將消息攔截器MyMessageInspector作如下修改:

      [csharp]?view plaincopy print?
    • ///?<summary>??
    • ///??消息攔截器??
    • ///?</summary>??
    • public?class?MyMessageInspector:IClientMessageInspector,IDispatchMessageInspector??
    • {??
    • ????void?IClientMessageInspector.AfterReceiveReply(ref?Message?reply,?object?correlationState)??
    • ????{??
    • ????????//Console.WriteLine("客戶端接收到的回復:\n{0}",?reply.ToString());??
    • ????????return;??
    • ????}??
    • ??
    • ????object?IClientMessageInspector.BeforeSendRequest(ref?Message?request,?IClientChannel?channel)??
    • ????{??
    • ????????//Console.WriteLine("客戶端發送請求前的SOAP消息:\n{0}",?request.ToString());??
    • ????????//?插入驗證信息??
    • ????????MessageHeader?hdUserName?=?MessageHeader.CreateHeader("u",?"fuck",?"admin");??
    • ????????MessageHeader?hdPassWord?=?MessageHeader.CreateHeader("p",?"fuck",?"123");??
    • ????????request.Headers.Add(hdUserName);??
    • ????????request.Headers.Add(hdPassWord);??
    • ????????return?null;??
    • ????}??
    • ??
    • ????object?IDispatchMessageInspector.AfterReceiveRequest(ref?Message?request,?IClientChannel?channel,?InstanceContext?instanceContext)??
    • ????{??
    • ????????//Console.WriteLine("服務器端:接收到的請求:\n{0}",?request.ToString());??
    • ????????//?栓查驗證信息??
    • ????????string?un?=?request.Headers.GetHeader<string>("u",?"fuck");??
    • ????????string?ps?=?request.Headers.GetHeader<string>("p",?"fuck");??
    • ????????if?(un?==?"admin"?&&?ps?==?"abcd")??
    • ????????{??
    • ????????????Console.WriteLine("用戶名和密碼正確。");??
    • ????????}??
    • ????????else??
    • ????????{??
    • ????????????throw?new?Exception("驗證失敗,滾吧!");??
    • ????????}??
    • ????????return?null;??
    • ????}??
    • ??
    • ????void?IDispatchMessageInspector.BeforeSendReply(ref?Message?reply,?object?correlationState)??
    • ????{??
    • ????????//Console.WriteLine("服務器即將作出以下回復:\n{0}",?reply.ToString());??
    • ????????return;??
    • ????}??
    • }??
    • 注意:添加對System.Runtime.Serialization的引用。

      創建消息頭時,第一個參數是名字,如上面的“u”,第二個參數是命名空間,這個可以自己來定義,比如上面的“fuck”,第三個參數就是消息頭的內容。


      現在重新生成一下項目,再試試。

      ?

      前面我們說過,如果安裝證書進行身份驗證會相當TMD麻煩,而可以通過修改SOAP消息頭來驗證,但是,上次的做法會有一個麻煩,那就是每次調用操作協定都要手動修改一次,這一次,我們直接在終結點級別進行修改和驗證,就省去了許多功夫。

總結

以上是生活随笔為你收集整理的传说中的WCF:消息拦截与篡改的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。