WCF 学习总结5 -- 消息拦截实现用户名验证(转)
WCF建立在基于消息的通信这一概念基础上。通过方法调用(Method Call)形式体现的服务访问需要转化成具体的消息,并通过相应的编码(Encoding)才能通过传输通道发送到服务端;服务操作执行的结果也只能以消息的形式才能被正常地返回到客户端。所以,消息在整个WCF体系结构中处于一个核心的地位,WCF可以看成是一个消息处理的管道,如下图所示:
WCF的一个操作(以及操作的参数)被序列化为Soap协议所支持的消息(XML结构),经过服务运行层,交给Binding中所定义的消息传递层,消息传递层由通道(Channel)组成。通道是以某种方式对消息进行处理(例如通过对消息进行身份验证)的组件,通道对消息和消息头进行操作,而服务运行层主要针对消息正文内容进行处理。 
方法一. 通过OperationContext直接添加/访问MessageHeader信息
使用OperationContext我们可以:访问当前操作执行环境。 特别是,操作上下文用于访问双工服务中的回调通道、存储整个操作部分的额外状态数据、访问传入消息头和属性以及添加传出消息头和属性。下面用代码演示下如何在MessageHeader中添加额外的信息,进行用户验证。
1. 服务契约
- using System;
- using System.Runtime.Serialization;
- using System.ServiceModel;
- namespace WcfSvcLib
- {
- [ServiceContract(Namespace="http://blog.csdn.net/fangxinggood")]
- public interface IService1
- {
- [OperationContract]
- string GetData(int value);
- }
- }
2. 服务实现
- using System;
- using System.Runtime.Serialization;
- using System.ServiceModel;
- namespace WcfSvcLib
- {
- public class Service1 : IService1
- {
- public string GetData(int value)
- {
- Console.WriteLine(OperationContext.Current.RequestContext.RequestMessage);
- // 注意namespace必须和ServiceContract中定义的namespace保持一致,默认是:http://tempuri.org
- var ns = "http://blog.csdn.net/fangxinggood";
- var user = GetHeaderValue("user", ns);
- var pwd = GetHeaderValue("pwd", ns);
- // 验证失败会抛出Invalid User的异常。
- if (user != "fangxing" || pwd != "password")
- throw new FaultException("Invalid User!");
- return string.Format("You entered: {0}", value);
- }
- private string GetHeaderValue(string name, string ns = "http://tempuri.org")
- {
- var headers = OperationContext.Current.IncomingMessageHeaders;
- var index = headers.FindHeader(name, ns);
- if (index > -1)
- return headers.GetHeader<string>(index);
- else
- return null;
- }
- }
- }
3. 客户端实现
- using System;
- using System.ServiceModel;
- using System.ServiceModel.Channels;
- namespace WcfClient
- {
- class Program
- {
- static void Main(string[] args)
- {
- var client = new WcfSvc.Service1Client();
- using (var scope = new OperationContextScope(client.InnerChannel))
- {
- // 注意namespace必须和ServiceContract中定义的namespace保持一致,默认是:http://tempuri.org
- var myNamespace = "http://blog.csdn.net/fangxinggood";
- // 注意Header的名字中不能出现空格,因为要作为Xml节点名。
- var user = MessageHeader.CreateHeader("user", myNamespace, "fangxing");
- var pwd = MessageHeader.CreateHeader("pwd", myNamespace, "password");
- OperationContext.Current.OutgoingMessageHeaders.Add(user);
- OperationContext.Current.OutgoingMessageHeaders.Add(pwd);
- var result = client.GetData(100);
- Console.WriteLine(result);
- Console.Read();
- }
- }
- }
- }
运行一下,在服务端通过 Console.WriteLine(OperationContext.Current.RequestContext.RequestMessage); 输出了请求的Message。通过输出的信息,我们可以看到Header里添加的信息: 
通过上面的代码,我们可以完成类似WebService的SoapHeader验证。但是这样需要我们每个契约都做类似的添加、验证,这样岂不是很繁琐。下面看方法二,通过消息检查器完成统一的用户验证。
方法二. 消息检查器方式添加/访问MessageHeader信息
客户端通过实现IClientMessageInspector接口,服务端通过实现IDispatchMessageInspector接口,来拦截消息。这种方式是通过扩展Behavior来加入拦截的,所以还需要分别实现IEndpointBehavior(客户端)和IServiceBehavior(服务端)接口,并通过配置将消息检查器加入。
工程结构:
实现说明:
【客户端】
1. ClientInterpector 实现:
- using System;
- using System.ServiceModel.Dispatcher;
- using System.ServiceModel.Configuration;
- using System.ServiceModel.Channels;
- using System.ServiceModel;
- namespace WcfClientInterpector
- {
- public class ClientInterpector : IClientMessageInspector
- {
- public void AfterReceiveReply(ref Message reply, object correlationState)
- {
- }
- public object BeforeSendRequest(ref Message request, IClientChannel channel)
- {
- var userNameHeader = MessageHeader.CreateHeader("OperationUserName", "http://tempuri.org", "fangxing", false, "");
- var pwdNameHeader = MessageHeader.CreateHeader("OperationPwd", "http://tempuri.org", "password", false, "");
- request.Headers.Add(userNameHeader);
- request.Headers.Add(pwdNameHeader);
- Console.WriteLine(request);
- return null;
- }
- }
- }
2. MyClientBehavior 实现: (实现扩展endpointBehavior元素)
- using System;
- using System.ServiceModel.Configuration;
- using System.ServiceModel.Description;
- using System.ServiceModel.Dispatcher;
- using System.ServiceModel;
- namespace WcfClientInterpector
- {
- public class MyClientBehavior : BehaviorExtensionElement, IEndpointBehavior
- {
- public override Type BehaviorType
- {
- get { return typeof(MyClientBehavior); }
- }
- protected override object CreateBehavior()
- {
- return new MyClientBehavior();
- }
- #region IEndpointBehavior Members
- public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
- {
- }
- public void ApplyClientBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
- {
- clientRuntime.MessageInspectors.Add(new ClientInterpector());
- }
- public void ApplyDispatchBehavior(ServiceEndpoint endpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
- {
- }
- public void Validate(ServiceEndpoint endpoint)
- {
- }
- #endregion
- }
- }
3. 配置
修改客户端配置文件,步骤如下:
(1) 在Advanced>Extensions>behavior element extensions中加入自定义的ClientInterpector。
(2) 在Advanced>Endpoint Behaviors中定义一个Behavior,添加上面配置过的extension
(3) 修改Client>Endpoints下的Endpoint的Behavior Config指向(2)配置的Behavior。 
【服务端】
1. ServiceInterpector 实现:
- using System;
- using System.ServiceModel.Dispatcher;
- using System.ServiceModel;
- namespace WcfServiceInterpector
- {
- public class ServiceInterpector : IDispatchMessageInspector
- {
- #region IDispatchMessageInspector Members
- public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
- {
- Console.WriteLine(request);
- var user = GetHeaderValue("OperationUserName");
- var pwd = GetHeaderValue("OperationPwd");
- if (user != "fangxing" || pwd != "password")
- throw new FaultException("Invalid User!");
- return null;
- }
- public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
- {
- }
- private string GetHeaderValue(string name, string ns = "http://tempuri.org")
- {
- var headers = OperationContext.Current.IncomingMessageHeaders;
- var index = headers.FindHeader(name, ns);
- if (index > -1)
- return headers.GetHeader<string>(index);
- else
- return null;
- }
- #endregion
- }
- }
2. MyServiceBehavior 实现:(实现扩展serviceBehavior元素)
- using System;
- using System.ServiceModel.Configuration;
- using System.ServiceModel.Description;
- using System.ServiceModel.Dispatcher;
- namespace WcfServiceInterpector
- {
- public class MyServiceBehavior : BehaviorExtensionElement, IServiceBehavior
- {
- public override Type BehaviorType
- {
- get { return typeof(MyServiceBehavior); }
- }
- protected override object CreateBehavior()
- {
- return new MyServiceBehavior();
- }
- #region IServiceBehavior Members
- public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
- {
- }
- public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
- {
- foreach (ChannelDispatcher chDisp in serviceHostBase.ChannelDispatchers)
- {
- foreach (EndpointDispatcher epDisp in chDisp.Endpoints)
- {
- epDisp.DispatchRuntime.MessageInspectors.Add(new ServiceInterpector());
- }
- }
- }
- public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
- {
- }
- #endregion
- }
- }
3. 配置
修改服务端配置文件,步骤如下:
(1) 在Advanced>Extensions>behavior element extensions中加入自定义的ServiceInterpector。
(2) 在Advanced>Service Behaviors中定义一个Behavior,添加上面配置过的extension
(3) 修改Services下的服务节点的Behavior Config指向(2)配置的Behavior。 
WCF 学习总结5 -- 消息拦截实现用户名验证(转)的更多相关文章
- WCF消息拦截,利用消息拦截做身份验证服务
本文参考 http://blog.csdn.net/tcjiaan/article/details/8274493 博客而写 添加对信息处理的类 /// <summary> /// 消 ...
- WCF学习笔记之消息交换模式
在WCF通信中,有三种消息交换模式,OneWay(单向模式), Request/Reponse(请求回复模式), Duplex(双工通信模式)这三种通信方式.下面对这三种消息交换模式进行讲解. 1. ...
- 【WCF安全】SOAP消息实现用户名验证:通过OperationContext直接添加/访问MessageHeader信息
服务代码 1.契约 using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Se ...
- 重温WCF之消息拦截与篡改(八)
我们知道,在WCF中,客户端对服务操作方法的每一次调用,都可以被看作是一条消息,而且,可能我们还会有一个疑问:如何知道客户端与服务器通讯过程中,期间发送和接收的SOAP是什么样子.当然,也有人是通过借 ...
- 传说中的WCF(10):消息拦截与篡改
我们知道,在WCF中,客户端对服务操作方法的每一次调用,都可以被看作是一条消息,而且,可能我们还会有一个疑问:如何知道客户端与服务器通讯过 程中,期间发送和接收的SOAP是什么样子.当然,也有人是通过 ...
- 传说中的WCF:消息拦截与篡改
我们知道,在WCF中,客户端对服务操作方法的每一次调用,都可以被看作是一条消息,而且,可能我们还会有一个疑问:如何知道客户端与服务器通讯过程中,期间发送和接收的SOAP是什么样子.当然,也有人是通过借 ...
- WCF学习之旅—第三个示例之二(二十八)
上接WCF学习之旅—第三个示例之一(二十七) 五.在项目BookMgr.Model创建实体类数据 第一步,安装Entity Framework 1) 使用NuGet下载最新版的Entity Fram ...
- WCF学习之旅—WCF服务的Windows 服务程序寄宿(十一)
上接 WCF学习之旅—WCF服务部署到IIS7.5(九) WCF学习之旅—WCF服务部署到应用程序(十) 七 WCF服务的Windows 服务程序寄宿 这种方式的服务寄宿,和IIS一样有一个一样 ...
- WCF学习之旅—TcpTrace工具(二十六)
止文(WCF学习之旅—TcpTrace工具(二十五))介绍了关于TcpTrance的一种使用方式,接下来介绍第二种使用方式. 三.通过ListenUri实现基于tcpTracer的消息路由 对于路由的 ...
随机推荐
- JAVA设计模式(一)单例模式
单例设计模式 Singleton是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点. 核心知识点如下: (1) 将采用单例 ...
- (C/C++学习笔记)附页: C/C++各数据类型的相关说明
- Linux学习: 触摸屏驱动
一.Linux输入子系统的结构: 二.触摸屏驱动代码: s3c_ts.c #include <linux/errno.h> #include <linux/kernel.h> ...
- Comparable和Comparator接口是干什么的?列出它们的区别。
Comparable和Comparator接口是干什么的?列出它们的区别. Java提供了只包含一个compareTo()方法的Comparable接口.这个方法可以个给两个对象排序.具体来说,它返回 ...
- 使用AWR报告诊断Oracle性能问题
在做单交易负载测试时,有的交易响应时间超出了指标值,在排除完测试环境等可能造成交易超时的原因后,去分析数据库问题.数据库用的是Oracle,对于Oracle数据库整体的性能问题, awr的报告是一个非 ...
- SQL-8 找出所有员工当前(to_date='9999-01-01')具体的薪水salary情况,对于相同的薪水只显示一次,并按照逆序显示
题目描述 找出所有员工当前(to_date='9999-01-01')具体的薪水salary情况,对于相同的薪水只显示一次,并按照逆序显示CREATE TABLE `salaries` (`emp_n ...
- SharePoint Framework 配置你的SharePoint客户端web部件开发环境
博客地址:http://blog.csdn.net/FoxDave 你可以使用Visual Studio或者是你自己的开发环境来构建SharePoint客户端web部件.你可以使用Mac.PC或是 ...
- rnn-手写数字识别-网络结构-shape
手写数字识别经典案例,目标是: 1. 掌握tf编写RNN的方法 2. 剖析RNN网络结构 tensorflow编程 #coding:utf-8 import tensorflow as tf from ...
- chromium ③
chromium源码学习笔记(1) -- 学习计划 对于c++开发者来说,chromium几乎是目前最值得学习的优秀开源代码.先看看chromium包含了多少令人激动的特性: 1, ...
- FileInputStream类与FileOutputStream类
FileInputStream类是InputStream类的子类.他实现了文件的读取,是文件字节输入流.该类适用于比较简单的文件读取,其所有方法都是从InputStream类继承并重写的.创建文件字节 ...