WCF建立在基于消息的通信这一概念基础上。通过方法调用(Method Call)形式体现的服务访问需要转化成具体的消息,并通过相应的编码(Encoding)才能通过传输通道发送到服务端;服务操作执行的结果也只能以消息的形式才能被正常地返回到客户端。所以,消息在整个WCF体系结构中处于一个核心的地位,WCF可以看成是一个消息处理的管道,如下图所示: 
  
WCF的一个操作(以及操作的参数)被序列化为Soap协议所支持的消息(XML结构),经过服务运行层,交给Binding中所定义的消息传递层,消息传递层由通道(Channel)组成。通道是以某种方式对消息进行处理(例如通过对消息进行身份验证)的组件,通道对消息和消息头进行操作,而服务运行层主要针对消息正文内容进行处理。 

方法一. 通过OperationContext直接添加/访问MessageHeader信息 
使用OperationContext我们可以:访问当前操作执行环境。 特别是,操作上下文用于访问双工服务中的回调通道、存储整个操作部分的额外状态数据、访问传入消息头和属性以及添加传出消息头和属性。下面用代码演示下如何在MessageHeader中添加额外的信息,进行用户验证。 
1. 服务契约

  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. 服务实现

  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. 客户端实现

  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里添加的信息: 

通过上面的代码,我们可以完成类似WebService的SoapHeader验证。但是这样需要我们每个契约都做类似的添加、验证,这样岂不是很繁琐。下面看方法二,通过消息检查器完成统一的用户验证。

方法二. 消息检查器方式添加/访问MessageHeader信息 
客户端通过实现IClientMessageInspector接口,服务端通过实现IDispatchMessageInspector接口,来拦截消息。这种方式是通过扩展Behavior来加入拦截的,所以还需要分别实现IEndpointBehavior(客户端)和IServiceBehavior(服务端)接口,并通过配置将消息检查器加入。 
工程结构: 
  实现说明: 
【客户端】
 
1. ClientInterpector 实现:

  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元素)

  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。 
  
(2) 在Advanced>Endpoint Behaviors中定义一个Behavior,添加上面配置过的extension 
  
(3) 修改Client>Endpoints下的Endpoint的Behavior Config指向(2)配置的Behavior。 

【服务端】 
1. ServiceInterpector 实现:

  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元素)

  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。 
  
(2) 在Advanced>Service Behaviors中定义一个Behavior,添加上面配置过的extension 
  
(3) 修改Services下的服务节点的Behavior Config指向(2)配置的Behavior。 

WCF 学习总结5 -- 消息拦截实现用户名验证(转)的更多相关文章

  1. WCF消息拦截,利用消息拦截做身份验证服务

    本文参考  http://blog.csdn.net/tcjiaan/article/details/8274493  博客而写 添加对信息处理的类 /// <summary> /// 消 ...

  2. WCF学习笔记之消息交换模式

    在WCF通信中,有三种消息交换模式,OneWay(单向模式), Request/Reponse(请求回复模式), Duplex(双工通信模式)这三种通信方式.下面对这三种消息交换模式进行讲解. 1. ...

  3. 【WCF安全】SOAP消息实现用户名验证:通过OperationContext直接添加/访问MessageHeader信息

    服务代码 1.契约 using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Se ...

  4. 重温WCF之消息拦截与篡改(八)

    我们知道,在WCF中,客户端对服务操作方法的每一次调用,都可以被看作是一条消息,而且,可能我们还会有一个疑问:如何知道客户端与服务器通讯过程中,期间发送和接收的SOAP是什么样子.当然,也有人是通过借 ...

  5. 传说中的WCF(10):消息拦截与篡改

    我们知道,在WCF中,客户端对服务操作方法的每一次调用,都可以被看作是一条消息,而且,可能我们还会有一个疑问:如何知道客户端与服务器通讯过 程中,期间发送和接收的SOAP是什么样子.当然,也有人是通过 ...

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

    我们知道,在WCF中,客户端对服务操作方法的每一次调用,都可以被看作是一条消息,而且,可能我们还会有一个疑问:如何知道客户端与服务器通讯过程中,期间发送和接收的SOAP是什么样子.当然,也有人是通过借 ...

  7. WCF学习之旅—第三个示例之二(二十八)

    上接WCF学习之旅—第三个示例之一(二十七) 五.在项目BookMgr.Model创建实体类数据 第一步,安装Entity Framework 1)  使用NuGet下载最新版的Entity Fram ...

  8. WCF学习之旅—WCF服务的Windows 服务程序寄宿(十一)

    上接    WCF学习之旅—WCF服务部署到IIS7.5(九) WCF学习之旅—WCF服务部署到应用程序(十) 七 WCF服务的Windows 服务程序寄宿 这种方式的服务寄宿,和IIS一样有一个一样 ...

  9. WCF学习之旅—TcpTrace工具(二十六)

    止文(WCF学习之旅—TcpTrace工具(二十五))介绍了关于TcpTrance的一种使用方式,接下来介绍第二种使用方式. 三.通过ListenUri实现基于tcpTracer的消息路由 对于路由的 ...

随机推荐

  1. [HAOI2006]l旅行

    这道题...一眼看出一个暴力思虑...那就是按照生成树... 排完序之后从当前边开始向后做生成树... 统计一下答案就好了... 结果...这就是正解...QVQ...smg...我去... 呆码: ...

  2. DevExpress ASP.NET Bootstrap Controls v18.2新功能详解(一)

    行业领先的.NET界面控件2018年第二次重大更新——DevExpress v18.2日前正式发布,本站将以连载的形式为大家介绍新版本新功能.本文将介绍了DevExpress ASP.NET Boot ...

  3. Androi开发 ---Fragment片段布局案例

    效果图: 1.MainActivity.java package com.example.android_activity; import android.app.Activity; import a ...

  4. NioEventLoop(netty 4.1)

    里面有个excecutor属性, 在loopgroup实例化loop的时候, 如果execute一个runnable的task的时候,检测loop启动了没有,没启动的话,执行excecutor的exe ...

  5. 12.Python爬虫利器三之Xpath语法与lxml库的用法

    LXML解析库使用的是Xpath语法: XPath 是一门语言 XPath可以在XML文档中查找信息 XPath支持HTML XPath通过元素和属性进行导航 XPath可以用来提取信息 XPath比 ...

  6. mysql解决数据库高并发

    分表分库 数据库索引 redis缓存数据库 读写分离 负载均衡: 将大量的并发请求分担到多个处理节点,由于单个处理节点的故障不影响服务,负载均衡集群同事也实现了高可用性.

  7. hive中left join、left outer join和left semi join的区别

    先说结论,再举例子.   hive中,left join与left outer join等价.   left semi join与left outer join的区别:left semi join相当 ...

  8. PCMU G.711U/PCMA G.711A简介

    PCMA(G.711A) 类型:Audio 制定者:ITU-T 所需频宽:64Kbps(90.4) 特性:PCMU和PCMA都能提供较好的语音质量,但是它们占用的带宽较高,需要64kbps. 优点:语 ...

  9. Jsoup的学习

    一 . 什么是jsoup jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址.HTML文本内容.它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来 ...

  10. 大数据-07-Spark之流数据

    摘自 http://dblab.xmu.edu.cn/blog/1084-2/ 简介 DStream是Spark Streaming的编程模型,DStream的操作包括输入.转换和输出. Spark ...