WCF笔记(6)消息拦截与篡改
2013-03-24 16:23
471 查看
要对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)中,这样一来,服务器端和客户端都可以引用它。
示例代码
1、实现消息拦截器
2、实现服务端
3、实现客户端
消息拦截效果图
修改消息,只需要对消息拦截器(MessageIntercept)类做修改
前面我们说过,如果安装证书进行身份验证会相当麻烦,而可以通过修改SOAP消息头来验证,但是,上次的做法会有一个麻烦,那就是每次调用操作协定都要手动修改一次,这一次,我们直接在终结点级别进行修改和验证,就省去了许多功夫。
接口一:IClientMessageInspector
从名字中我们可以猜测,它是用来拦截客户消息的,而看看它的方法,你就更加肯定当初的猜测了。
BeforeSendRequest:向服务器发送请求前拦截或修改消息(事前控制)
AfterReceiveReply:接收到服务器的回复消息后,在调用返回之前拦截或修改消息(事后诸葛亮)
接口二:IDispatchMessageInspector
刚才那个接口是针对客户端的,而这个是针对服务器端的。
AfterReceiveRequest:接收客户端请求后,在进入操作处理代码之前拦截或修改消息(欺上)
BeforeSendReply:服务器向客户端发送回复消息之前拦截和修改消息(瞒下)。
虽然实现了这两个接口,但你会有新的疑问,怎么用?把它们放到哪儿才能拦截消息?因此,下一步就是要实现IEndpointBehavior按口(System.ServiceModel.Description命名空间,程序集System.ServiceModel),它有四个方法,而我们只需要处理两个就够了。
下面是MSDN的翻译版本说明:
使用 ApplyClientBehavior 方法可以在客户端应用程序中修改、检查或插入对终结点中的扩展。
使用 ApplyDispatchBehavior 方法可以在服务应用程序中修改、检查或插入对终结点范围执行的扩展。
我想不用额外解释了,说白了就是一个在客户拦截和修改消息,另一个在服务器端拦截和修改消息。
在实现这两个方法时,和前面我们实现的IClientMessageInspector和IDispatchMessageInspector联系起来就OK了。
做完了IEndpointBehavior的事情后,把它插入到服务终结点中就行了,无论是服务器端还是客户端,这一步都必须的,因为我们实现的拦截器是包括两个端的,因此,较好的做法是把这些类写到一个独立的类库(dll)中,这样一来,服务器端和客户端都可以引用它。
示例代码
1、实现消息拦截器
using System; using System.ServiceModel.Dispatcher; using System.ServiceModel.Description; namespace MyMessageIntercept { /// <summary> /// 消息拦截器【实现客户端和服务端的消息拦截】 /// </summary> public class MessageIntercept:IClientMessageInspector,IDispatchMessageInspector { public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { Console.WriteLine("客户端接收到的回复:{0}\n", reply); } public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) { Console.WriteLine("客户端发送请求前的SOAP消息:{0}\n", request); return null; } public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext) { Console.WriteLine("服务器端接收到的请求:{0}\n", request); return null; } public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { Console.WriteLine("服务器将作出以下回复:{0}\n", reply); } } /// <summary> /// 将自定义的消息拦截器(MessageIntercept)插入到终结点行为 /// </summary> public class MyEndPointBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { //不需要 } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { //植入"窃听器"客户端 clientRuntime.ClientMessageInspectors.Add(new MessageIntercept()); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { //植入"窃听器"服务端 endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MessageIntercept()); } public void Validate(ServiceEndpoint endpoint) { //不需要 } } }
2、实现服务端
using System; using System.ServiceModel; using System.ServiceModel.Description; using System.Runtime.Serialization; using MyMessageIntercept; namespace Server { class Program { //设置服务并启动 static void Main() { //服务基地址 Uri baseUri = new Uri("http://localhost:3000/Service"); //声明服务器主机 using (ServiceHost host = new ServiceHost(typeof(MyService), baseUri)) { //添加绑定和终结点 WSHttpBinding binding = new WSHttpBinding(); host.AddServiceEndpoint(typeof(IService), binding, "test"); //添加服务描述 host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true }); //把自定义的"MyEndPointBehavior"插入到终结点中 foreach (var endPoint in host.Description.Endpoints) { endPoint.EndpointBehaviors.Add(new MyEndPointBehavior()); } //启动服务 try { host.Open(); Console.WriteLine("服务已启动"); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.ReadKey(); //关闭服务 host.Close(); } } } /// <summary> /// 定义服务协定 /// </summary> [ServiceContract] public interface IService { [OperationContract] int AddInt(int a, int b); [OperationContract] Student GetStudent(); [OperationContract] CalculateResultResponse CaculateNumber(CalculateRequest request); } /// <summary> /// 实现服务协定 /// </summary> public class MyService : IService { public int AddInt(int a, int b) { return a + b; } public Student GetStudent() { Student st = new Student { Name = "小明", Age = 23 }; return st; } public CalculateResultResponse CaculateNumber(CalculateRequest request) { CalculateResultResponse result = new CalculateResultResponse(); switch (request.Operation) { case "+": result.CalculateResult = request.NumberA + request.NumberB; break; case "-": result.CalculateResult = request.NumberA - request.NumberB; break; case "*": result.CalculateResult = request.NumberA * request.NumberB; break; case "/": result.CalculateResult = request.NumberA / request.NumberB; break; default: throw new ArgumentException("运算操作符只允许'+'、'-'、'*'、'/'."); } return result; } } [DataContract] public class Student { [DataMember] public string Name { get; set; } [DataMember] public int Age { get; set; } } [MessageContract] public class CalculateRequest { /// <summary> /// 操作符 /// </summary> [MessageHeader] public string Operation { get; set; } /// <summary> /// 第一个数 /// </summary> [MessageBodyMember] public int NumberA { get; set; } /// <summary> /// 第二个数 /// </summary> [MessageBodyMember ] public int NumberB { get; set; } } /// <summary> /// 计算返回的结果 /// </summary> [MessageContract] public class CalculateResultResponse { /// <summary> /// 计算结果 /// </summary> [MessageBodyMember] public int CalculateResult { get; set; } } }
3、实现客户端
using System; using MyMessageIntercept; namespace Client { class Program { private static void Main() { WS.ServiceClient sc = new WS.ServiceClient(); //记得在客户端也要插入自定义的"MyEndPointBehavior" sc.Endpoint.EndpointBehaviors.Add(new MyEndPointBehavior()); try { // 1、调用带元数据参数和返回值的操作 Console.WriteLine("10和55相加的结果是:{0}\n", sc.AddInt(10, 55)); //2、调用带有数据协定的操作 WS.Student st = sc.GetStudent(); Console.WriteLine("-----学生信息--------\n姓名:{0}\n年龄:{1}\n", st.Name, st.Age); //3、调用带有消息协定的操作 Console.WriteLine("12乘以56的结果是:{0}\n", sc.CaculateNumber("*", 12, 56)); sc.Close(); } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.ReadKey(); } } }
消息拦截效果图
修改消息,只需要对消息拦截器(MessageIntercept)类做修改
using System; using System.ServiceModel.Dispatcher; using System.ServiceModel.Description; namespace MyMessageIntercept { /// <summary> /// 消息拦截器【实现客户端和服务端的消息拦截】 /// </summary> public class MessageIntercept:IClientMessageInspector,IDispatchMessageInspector { public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { //Console.WriteLine("客户端接收到的回复:\n{0}", reply); } public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel) { //Console.WriteLine("客户端发送请求前的SOAP消息:\n{0}", request); // (客服端)插入验证信息 System.ServiceModel.Channels.MessageHeader hdUserName = System.ServiceModel.Channels.MessageHeader.CreateHeader("user", "myTest", "admin"); System.ServiceModel.Channels.MessageHeader hdPassWord = System.ServiceModel.Channels.MessageHeader.CreateHeader("pw", "myTest", "123"); request.Headers.Add(hdUserName); request.Headers.Add(hdPassWord); return null; } public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext) { //Console.WriteLine("服务器端:接收到的请求:\n{0}", request); // (服务端)栓查验证信息 string un = request.Headers.GetHeader<string>("user", "myTest"); string ps = request.Headers.GetHeader<string>("pw", "myTest"); if (un == "admin" && ps == "123") { Console.WriteLine("用户名和密码正确."); } else { throw new Exception("验证失败!"); } return null; } public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState) { //Console.WriteLine("服务器即将作出以下回复:\n{0}", reply); } } /// <summary> /// 将自定义的消息拦截器(MessageIntercept)插入到终结点行为 /// </summary> public class MyEndPointBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { //不需要 } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { //植入"窃听器"客户端 clientRuntime.ClientMessageInspectors.Add(new MessageIntercept()); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { //植入"窃听器"服务端 endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new MessageIntercept()); } public void Validate(ServiceEndpoint endpoint) { //不需要 } } }注意:添加对System.Runtime.Serialization的引用。
前面我们说过,如果安装证书进行身份验证会相当麻烦,而可以通过修改SOAP消息头来验证,但是,上次的做法会有一个麻烦,那就是每次调用操作协定都要手动修改一次,这一次,我们直接在终结点级别进行修改和验证,就省去了许多功夫。
相关文章推荐
- 传说中的WCF(10):消息拦截与篡改
- 传说中的WCF(10):消息拦截与篡改
- 传说中的WCF(10):消息拦截与篡改
- 传说中的WCF(10):消息拦截与篡改
- 重温WCF之消息拦截与篡改(八)
- 传说中的WCF:消息拦截与篡改
- 传说中的WCF(10):消息拦截与篡改
- WCF笔记(3)发送和接收SOAP(消息)头
- WCF消息拦截,利用消息拦截做身份验证服务
- 通过定制行为拦截WCF消息
- 通过拦截WCF消息进行身份栈传播
- WCF 学习总结5 -- 消息拦截实现用户名验证
- WCF笔记(4)消息协定
- wcf 消息拦截
- WCF学习笔记之消息交换模式
- [WCF 学习笔记] 3. 消息交换
- [WCF 学习笔记] 3. 消息交换
- [WCF 学习笔记] 4. 消息操作
- [WCF 学习笔记] 4. 消息操作
- [WCF 学习笔记] 11. 配置文件