在Web应用中,我们可以使用Session或Cookies在浏览器与Server间维护会话状态及传递附加信息。假设有这样的需求,用户正常登陆系统后,Server产生一个UserIdentity发送到Client端,在此后的Client每次调用Service时都需要传递此UserIdentity传递到Server端以进行用户身份认证及权限审核。在WCF应用中有没有什么方法可以实现同样功能呢?
首先我们看看
MSDN
中对
WCF
的
Session
的说明:
它们由调用应用程序显式启动和终止。
会话期间传递的消息按照接收消息的顺序进行处理。
会话将一组消息相互关联,从而形成对话。
该关联的含义是抽象的。
例如,一个基于会话的通道可能会根据共享网络连接来关联消息,而另一个基于会话的通道可能会根据消息正文中的共享标记来关联消息。
可以从会话派生的功能取决于关联的性质。
不存在与
WCF
会话相关联的常规数据存储区。
最后一句告诉我们,
WCF
中的
Session
是无法像
Web
应用一样存储附加信息的。
经过研究,我们可以通过扩展
MessageHeader
实现一个附加的数据存储区在
Client
端每次请求
Service
时发送到
Server
端。具体实现如下(以前述需求为例)。
首先我们定义一个类作为交互过程中的
UserIdentity
,代码如下:
这里使用[Serializable]作为序列化方式。在这个类中,UserName对应用户名,UserRuningSystemID对应用户已登陆的系统ID,UserSysGUID对应用户当前的有效登陆。用户登录后Server产生这个类的一个实例回传Client,Client将其保存在缓存里,这里我定义了一个类来作为缓存,代码如下:
using
System;
using System.Collections.Generic;
using System.Text;
namespace BNCommonPlus.BNCommonModel
... {
[Serializable]
public class UserIdentity
...{
public string UserName = string.Empty;
public string UserRuningSystemID = string.Empty;
public string UserSysGUID = string.Empty;
}
}
using System.Collections.Generic;
using System.Text;
namespace BNCommonPlus.BNCommonModel
... {
[Serializable]
public class UserIdentity
...{
public string UserName = string.Empty;
public string UserRuningSystemID = string.Empty;
public string UserSysGUID = string.Empty;
}
}
这里使用[Serializable]作为序列化方式。在这个类中,UserName对应用户名,UserRuningSystemID对应用户已登陆的系统ID,UserSysGUID对应用户当前的有效登陆。用户登录后Server产生这个类的一个实例回传Client,Client将其保存在缓存里,这里我定义了一个类来作为缓存,代码如下:
using
System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using BNCommonPlus.BNCommonModel;
namespace BNIIClientLayerPlus
... {
public class UserPermissionInfo
...{
private UserPermissionInfo()
...{ }
private static UserPermissionInfo _Instance = null;
public static UserPermissionInfo GetInstance()
...{
if (_Instance == null)
...{
_Instance = new UserPermissionInfo();
}
return _Instance;
}
private UserIdentity _userIdentity = null;
public UserIdentity _UserIdentity
...{
get
...{
return _userIdentity;
}
}
public void SetUserIdentity(UserIdentity ui)
...{
_userIdentity = ui;
}
}
}
using System.Collections.Generic;
using System.Text;
using System.Data;
using BNCommonPlus.BNCommonModel;
namespace BNIIClientLayerPlus
... {
public class UserPermissionInfo
...{
private UserPermissionInfo()
...{ }
private static UserPermissionInfo _Instance = null;
public static UserPermissionInfo GetInstance()
...{
if (_Instance == null)
...{
_Instance = new UserPermissionInfo();
}
return _Instance;
}
private UserIdentity _userIdentity = null;
public UserIdentity _UserIdentity
...{
get
...{
return _userIdentity;
}
}
public void SetUserIdentity(UserIdentity ui)
...{
_userIdentity = ui;
}
}
}
这是一个单件类,Client正常登陆得到Server端回传的UserIdentity实例后可以通过如下代码将其存入缓存:
UserPermissionInfo.GetInstance().SetUserIdentity(ServerReturnedUserIdentity);
其中ServerReturnedUserIdentity就是Server产生并回传的UserIdentity
UserPermissionInfo.GetInstance().SetUserIdentity(ServerReturnedUserIdentity);
其中ServerReturnedUserIdentity就是Server产生并回传的UserIdentity
下面我们扩展
MessageHeader
将我们自己定义的
UserIdentity
加入进去,代码如下
:
using
System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceProcess;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
namespace BNCommon.ClientHelper
... {
public class BNClientMessageInspector : IClientMessageInspector
...{
IClientMessageInspector 成员#region IClientMessageInspector 成员
public void AfterReceiveReply(ref Message reply, object correlationState)
...{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
...{
MessageHeader mh = MessageHeader.CreateHeader("UserIdentity", "UINS", BNIIClientLayerPlus.UserPermissionInfo.GetInstance()._UserIdentity);
request.Headers.Add(mh);
return null;
}
#endregion
}
}
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceProcess;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
namespace BNCommon.ClientHelper
... {
public class BNClientMessageInspector : IClientMessageInspector
...{
IClientMessageInspector 成员#region IClientMessageInspector 成员
public void AfterReceiveReply(ref Message reply, object correlationState)
...{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
...{
MessageHeader mh = MessageHeader.CreateHeader("UserIdentity", "UINS", BNIIClientLayerPlus.UserPermissionInfo.GetInstance()._UserIdentity);
request.Headers.Add(mh);
return null;
}
#endregion
}
}
这个类实现了
IClientMessageInspector
接口,实现该接口可以在
Client
每次向
Server
请求前及请求返回后控制
Client
的行为对发送和接收的数据进行处理。
现在我们需要实现BehaviorExtensionElement, IEndpointBehavior将刚刚建立的行为加入Client行为集合,代码如下:
using
System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceProcess;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
namespace BNCommon.ClientHelper
... {
public class BNClientEndpointBehavior : BehaviorExtensionElement, IEndpointBehavior
...{
IEndpointBehavior 成员#region IEndpointBehavior 成员
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
...{}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
...{
clientRuntime.MessageInspectors.Add(new BNClientMessageInspector());
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
...{
}
public void Validate(ServiceEndpoint endpoint)
...{
return;
}
#endregion
public override Type BehaviorType
...{
get ...{ return typeof(BNClientEndpointBehavior); }
}
protected override object CreateBehavior()
...{
return new BNClientEndpointBehavior();
}
}
}
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceProcess;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
namespace BNCommon.ClientHelper
... {
public class BNClientEndpointBehavior : BehaviorExtensionElement, IEndpointBehavior
...{
IEndpointBehavior 成员#region IEndpointBehavior 成员
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
...{}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
...{
clientRuntime.MessageInspectors.Add(new BNClientMessageInspector());
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
...{
}
public void Validate(ServiceEndpoint endpoint)
...{
return;
}
#endregion
public override Type BehaviorType
...{
get ...{ return typeof(BNClientEndpointBehavior); }
}
protected override object CreateBehavior()
...{
return new BNClientEndpointBehavior();
}
}
}
现在我们定义了
Client
在用户正常登陆后,每次请求都会发送缓存里面的
UserIdentity
实例。然后需要修改
Client
端的代理类,这里要分两种情况:如果客户端使用的是
”SvcUtil.exe”
工具生成的代理类,则需要修改对应
Client
的构造方法,加入如下代码:
Endpoint.Behaviors.Add(new BNCommonPlus.ClientHelper.BNClientEndpointBehavior());
如果使用ChanelFactory则需要在Create对应得ChanelFactory后加入如下代码:
_ChannelFactory.Endpoint.Behaviors.Add(new BNClientEndpointBehavior());
当然也可以通过配置文件实现上述目的。
搞定Client端后,再来看看Server端如何取Client每次发送过来的附加信息.
首先在Server端要实现IDispatchMessageInspector接口,代码如下:
using
System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using BNCommonPlus.BNCommonModel;
namespace BNCommonPlus.ServiceHelper
... {
public class DispatchMessageInspector : IDispatchMessageInspector
...{
IDispatchMessageInspector 成员#region IDispatchMessageInspector 成员
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
...{
//得到身份信息
UserIdentity ui = request.Headers.GetHeader<UserIdentity>("UserIdentity", "UINS");
……..//这里写验证用户身份,检测权限的代码。
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
...{
}
#endregion
}
}
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using BNCommonPlus.BNCommonModel;
namespace BNCommonPlus.ServiceHelper
... {
public class DispatchMessageInspector : IDispatchMessageInspector
...{
IDispatchMessageInspector 成员#region IDispatchMessageInspector 成员
public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
...{
//得到身份信息
UserIdentity ui = request.Headers.GetHeader<UserIdentity>("UserIdentity", "UINS");
……..//这里写验证用户身份,检测权限的代码。
return null;
}
public void BeforeSendReply(ref Message reply, object correlationState)
...{
}
#endregion
}
}
和客户端一样将上面定义的行为加入行为集合,代码如下:
using
System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using BNCommonPlus.BNCommonModel;
namespace BNCommonPlus.ServiceHelper
... {
public class BNServiceOperationBehavior : BehaviorExtensionElement, IEndpointBehavior
...{
IEndpointBehavior 成员#region IEndpointBehavior 成员
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
...{ }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
...{ }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
...{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new DispatchMessageInspector());
}
public void Validate(ServiceEndpoint endpoint)
...{
}
public override Type BehaviorType
...{
get ...{ return typeof(BNServiceOperationBehavior); }
}
protected override object CreateBehavior()
...{
return new BNServiceOperationBehavior();
}
#endregion
}
}
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using BNCommonPlus.BNCommonModel;
namespace BNCommonPlus.ServiceHelper
... {
public class BNServiceOperationBehavior : BehaviorExtensionElement, IEndpointBehavior
...{
IEndpointBehavior 成员#region IEndpointBehavior 成员
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
...{ }
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
...{ }
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
...{
endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new DispatchMessageInspector());
}
public void Validate(ServiceEndpoint endpoint)
...{
}
public override Type BehaviorType
...{
get ...{ return typeof(BNServiceOperationBehavior); }
}
protected override object CreateBehavior()
...{
return new BNServiceOperationBehavior();
}
#endregion
}
}
最后将扩展出来的行为加入到ServiceHost上:
for
(int j = 0; j < _ServiceHost.Description.Endpoints.Count; j++)
{
_ServiceHost.Description.Endpoints[j].Behaviors.Add(new BNServiceOperationBehavior());
}