实现分布式的Membership和上下文传递_第1页
实现分布式的Membership和上下文传递_第2页
实现分布式的Membership和上下文传递_第3页
实现分布式的Membership和上下文传递_第4页
实现分布式的Membership和上下文传递_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、实现分布式的Membership和上下文传递通过上一篇了解了模块内基本的层次划分之后,接下来我们来聊聊PetShop中一些基本基础功能的实现,以及一些设计、架构上的应用如何同WCF进行集成。本篇讨论两个问题:实现分布式的Membership和客户端到服务端上下文(Context)的传递。一、 如何实现用户验证对登录用户的验证是大部分应用所必需的,对于ASP.NET来说,用户验证及帐号管理实现在成员资格(Membership)模块中。同ASP.NET的其他模块一样,微软在设计Membership的时候,为了实现更好地可扩展性,采用了策略(Strategy)设计模式:将模块相关的功能定义在被称为P

2、rovider的抽象类型中,并通过继承它提供具体的Provider。如果这些原生的Provider不能满足你的需求,你也可以通过继承该抽象的Provider,创建自定义的Provider。通过ASP.NET提供的配置,你可以很轻易地把自定义的Provider应用到你的应用之中。在一般情况下,最终的编程人员并不通过Provider调用相关的功能,而是通过一个外观(Facade)类实现对相关功能的调用。ASP.NET成员资格模块的设计基本上可以通过下面的类图1反映出来:最终的编程人员通过外观类型(Faade Class)Membership调用成员资格相关的功能,比如用户认证、用户注册、修改密码等

3、;Membership通过抽象类MembershipProvider提供所有的功能,至于最终的实现,则定义在一个个具体的MembershipProvider中。基于成员资格信息不同的存储方式,ASP.NET提供了两个原生的MembershipProvider:SqlMembershipProvider和ActiveDirectoryMembershipProvider,前者基于SQL Server数据库,后者基于AD。如果这两个MembershipProvider均不能满足需求,我们还可以自定义MembershipProvider。clip_image002图1 ASP.NET Members

4、hip 设计原理我们的案例并不会部署于AD之中,所以不能使用ActiveDirectoryMembershipProvider;直接通过Web服务器进行数据库的存取又不符合上述物理部署的要求(通过应用服务器进行数据库访问),所以SqlMembershipProvider也不能为我们所用。为此需要自定义MembershipProvider,通过WCF服务调用的形式提供成员资格所有功能的实现。我们将该自定义MembershipProvider称为RemoteMembershipProvider。图2揭示了RemoteMembershipProvider实现的原理:RemoteMembershipP

5、rovider通过调用WCF服务MembershipService提供对成员资格所有功能的实现;MembershipService则通过调用Membership实现服务;最终的实现还是落在了SqlMembershipProvider这个原生的MembershipProvider上。clip_image004图2 RemoteMembershipProvider实现原理1、服务契约和服务实现首先来看看MembershipService实现的服务契约的定义。由于MembershipService最终是为RemoteMembershipProvider这个自定义MembershipProvider服

6、务的,所以服务操作的定义是基于MembershipProvider的API定义。MembershipProvider包含两种类型的成员:属性和方法,简单起见,我们可以为MembershipProvider每一个抽象方法定义一个匹配的服务操作;而对于所有属性,完全采用服务端(应用服务器)的MembershipProvider相关属性。在RemoteMembershipProvider初始化的时候通过调用MembershipService获取所有服务端MembershipProvider的配置信息。为此,我们为MembershipProvider的所有属性定义了一个数据契约:MembershipC

7、onfigData。在PetShop中,MembershipConfigData和服务契约一起定义在Infrastructures.Service.Interface项目中。 1: using System.Runtime.Serialization; 2: using System.Web.Security; 3: namespace Artech.PetShop.Infrastructures.Service.Interface 4: 5: DataContract(Namespace = 6: public class MembershipConfigData 7: 8: DataMem

8、ber 9: public string ApplicationName 10: get; set; 11: 12: DataMember 13: public bool EnablePasswordReset 14: get; set; 15: 16: DataMember 17: public bool EnablePasswordRetrieval 18: get; set; 19: 20: DataMember 21: public int MaxInvalidPasswordAttempts 22: get; set; 23: 24: DataMember 25: public in

9、t MinRequiredNonAlphanumericCharacters 26: get; set; 27: 28: DataMember 29: public int MinRequiredPasswordLength 30: get; set; 31: 32: DataMember 33: public int PasswordAttemptWindow 34: get; set; 35: 36: DataMember 37: public MembershipPasswordFormat PasswordFormat 38: get; set; 39: 40: DataMember

10、41: public string PasswordStrengthRegularExpression 42: get; set; 43: 44: DataMember 45: public bool RequiresQuestionAndAnswer 46: get; set; 47: 48: DataMember 49: public bool RequiresUniqueEmail 50: get; set; 51: 52: 在服务契约中,定义了一个额外的方法GetMembershipConfigData获取服务端MembershipProvider的所有配置信息,而对于服务操作的定义,

11、则与MembershipProvider同名抽象方法相对应。 1: using System.ServiceModel; 2: using System.Web.Security; 3: namespace Artech.PetShop.Infrastructures.Service.Interface 4: 5: ServiceContract(Namespace= 6: public interface IMembershipService 7: 8: OperationContract 9: bool ChangePassword(string username, string oldP

12、assword, string newPassword); 10: OperationContract 11: bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer); 12: OperationContract 13: MembershipUser CreateUser(string username, string password, string email, string passwordQue

13、stion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status); 14: OperationContract 15: bool DeleteUser(string username, bool deleteAllRelatedData); 16: OperationContract 17: MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, in

14、t pageSize, out int totalRecords); 18: OperationContract 19: MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords); 20: OperationContract 21: MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords); 22: Ope

15、rationContract 23: int GetNumberOfUsersOnline(); 24: OperationContract 25: string GetPassword(string username, string answer); 26: OperationContract(Name=GetUserByName) 27: MembershipUser GetUser(string username, bool userIsOnline); 28: OperationContract(Name=GetUserByID) 29: MembershipUser GetUser(

16、object providerUserKey, bool userIsOnline); 30: OperationContract 31: string GetUserNameByEmail(string email); 32: OperationContract 33: string ResetPassword(string username, string answer); 34: OperationContract 35: bool UnlockUser(string userName); 36: OperationContract 37: void UpdateUser(Members

17、hipUser user); 38: OperationContract 39: bool ValidateUser(string username, string password); 40: OperationContract 41: MembershipConfigData GetMembershipConfigData(); 42: 43: 服务的实现,则异常简单,我们须要做的仅仅是通过Membership.Provider获得当前的MembershipProvider,调用同名的属性或方法即可。MembershipService定义在Infrastructures.Service中,

18、定义如下: 1: using System.Web.Security; 2: using Artech.PetShop.Infrastructures.Service.Interface; 3: namespace Artech.PetShop.Infrastructures.Service 4: 5: public class MembershipService : IMembershipService 6: 7: #region IMembershipService Members 8: 9: public bool ChangePassword(string username, stri

19、ng oldPassword, string newPassword) 10: 11: return Membership.Provider.ChangePassword(username, oldPassword, newPassword); 12: 13: 14: public bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer) 15: 16: return Membership.Provide

20、r.ChangePasswordQuestionAndAnswer(username, password, newPasswordQuestion, newPasswordAnswer); 17: 18: /其他成员 19: public MembershipConfigData GetMembershipConfigData() 20: 21: return new MembershipConfigData 22: 23: ApplicationName = Membership.Provider.ApplicationName, 24: EnablePasswordReset = Memb

21、ership.Provider.EnablePasswordReset, 25: EnablePasswordRetrieval = Membership.Provider.EnablePasswordRetrieval, 26: MaxInvalidPasswordAttempts = Membership.Provider.MaxInvalidPasswordAttempts, 27: MinRequiredNonAlphanumericCharacters = Membership.Provider.MinRequiredNonAlphanumericCharacters, 28: Mi

22、nRequiredPasswordLength = Membership.Provider.MinRequiredPasswordLength, 29: PasswordAttemptWindow = Membership.Provider.PasswordAttemptWindow, 30: PasswordFormat = Membership.Provider.PasswordFormat, 31: PasswordStrengthRegularExpression = Membership.Provider.PasswordStrengthRegularExpression, 32:

23、RequiresQuestionAndAnswer = Membership.Provider.RequiresQuestionAndAnswer, 33: RequiresUniqueEmail = Membership.Provider.RequiresUniqueEmail 34: ; 35: 36: 37: #endregion 38: 39: 2、RemoteMembershipProvider的实现由于RemoteMembershipProvider完全通过调用WCF服务的方式提供对所有成员资格功能的实现,所以进行RemoteMembershipProvider配置时,配置相应的终

24、结点就可以了。 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 在RemoteMembershipProvider中,通过Initialize方法获取配置的终结点名称并创建服务代理。通过该代理调用GetMembershipConfigData操作获取服务端MembershipProvider的配置信息,并对RemoteMembershipProvider进行初始化,RemoteMembershipProvider定义如下: 1: using System.Collections.Specialized; 2: using System.Co

25、nfiguration; 3: using System.Linq; 4: using System.Web.Security; 5: using Artech.PetShop.Common; 6: using Artech.PetShop.Infrastructures.Service.Interface; 7: 8: namespace Artech.PetShop.Infrastructures 9: 10: public class RemoteMembershipProvider : MembershipProvider 11: 12: private bool _enablePas

26、swordReset; 13: private bool _enablePasswordRetrieval; 14: /其他字段成员 15: 16: public IMembershipService MembershipProxy 17: get; private set; 18: 19: public override int MaxInvalidPasswordAttempts 20: 21: get return this._maxInvalidPasswordAttempts; 22: 23: 24: /其他属性成员 25: public override void Initiali

27、ze(string name, NameValueCollection config) 26: 27: if (!config.AllKeys.Contains(endpoint) 28: 29: throw new ConfigurationErrorsException(Missing the mandatory endpoint configuraiton property.); 30: 31: 32: this.MembershipProxy = ServiceProxyFactory.Create(configendpoint); 33: base.Initialize(name,

28、config); 34: MembershipConfigData configData = this.MembershipProxy.GetMembershipConfigData(); 35: this.ApplicationName = configData.ApplicationName; 36: this._enablePasswordReset = configData.EnablePasswordReset; 37: this._enablePasswordRetrieval = configData.EnablePasswordRetrieval; 38: /. 39: 40:

29、 41: 对于其他抽象方法的实现,仅仅须要通过上面创建的服务代理,调用相应的服务操作即可。注: 为了避免在服务操作调用后频繁地进行服务代理的关闭(Close)和终止(Abort)操作,我们采用基于AOP的方式实现服务的调用,将这些操作封装到一个自定义的RealProxy中,并通过ServiceProxyFactory创建该RealProxy的TransparentProxy。相关实现可以参考WCF技术剖析(卷1)第九章。二、 上下文的共享及跨域传递在进行基于N-Tier的应用开发中,我们往往需要在多个层次之间共享一些上下文(Context)信息,比如当前用户的Profile信息;在进行远程服务

30、调用时,也经常需要进行上下文信息的跨域传递。比如在PetShop中,服务端进行审核(Audit)的时候,须要获取当前登录的用户名。而登录用户名仅仅对于Web服务器可得,所以在每次服务调用的过程中,需要从客户端向服务端传递。1、ApplicationContext基于上下文的共享,我创建了一个特殊的类型:ApplicationContext。ApplicationContext定义在Common项目中,简单起见,直接将其定义成字典的形式。至于上下文数据的真正存储,如果当前HttpContext存在,将其存储与HttpSessionState中,否则将其存储于CallContext中。注: 由于C

31、allConext将数据存储于当前线程的TLS(Thread Local Storage)中,实际上HttpContext最终也采用这样的存储方式,所以ApplicaitonContext并不提供上下文信息跨线程的传递。 1: using System.Collections.Generic; 2: using System.Runtime.Remoting.Messaging; 3: using System.Web; 4: namespace Artech.PetShop.Common 5: 6: public class ApplicationContext:Dictionary 7:

32、8: public const string ContextKey = Artech.PetShop.Infrastructures.ApplicationContext; 9: public const string ContextHeaderLocalName = ApplicationContext; 10: public const string ContextHeaderNamespace = 11: public static ApplicationContext Current 12: 13: get 14: 15: if (HttpContext.Current != null

33、) 16: 17: if (HttpContext.Current.SessionContextKey = null) 18: 19: HttpContext.Current.SessionContextKey = new ApplicationContext(); 20: 21: 22: return HttpContext.Current.SessionContextKey as ApplicationContext; 23: 24: 25: if (CallContext.GetData(ContextKey) = null) 26: 27: CallContext.SetData(Co

34、ntextKey, new ApplicationContext(); 28: 29: 30: return CallContext.GetData(ContextKey) as ApplicationContext; 31: 32: set 33: 34: if (HttpContext.Current != null) 35: 36: HttpContext.Current.SessionContextKey = value; ; 37: 38: else 39: 40: CallContext.SetData(ContextKey, value); 41: 42: 43: 44: pub

35、lic string UserName 45: 46: get 47: 48: if (!this.ContainsKey(_UserName ) 49: 50: return string.Empty; 51: 52: 53: return (string)this_UserName; 54: 55: set 56: 57: this_UserName = value; 58: 59: 60: 61: 2、ApplicationContext在WCF服务调用中的传递下面我们来介绍一下如何实现上下文信息在WCF服务调用过程中的“隐式”传递。在PetShop中,我们通过WCF的扩展实现此项功能。

36、上下文传递的实现原理很简单:在客户端,将序列化后的当前上下文信息置于出栈(Outgoing)消息的SOAP报头中,并为报头指定一个名称和命名空间;在服务端,在服务操作执行之前,通过报头名称和命名空间将上下文SOAP报头从入栈(Incoming)消息中提取出来,进行反序列化,并将其设置成服务端当前的上下文。所以,上下文的传递实际上包含两个方面:SOAP报头的添加和提取。我们通过两个特殊的WCF对象来分别实现这两个功能:ClientMessageInspector和CallContextInitializer,前者在客户端将上下文信息封装成SOAP报头,并将其添加到出栈消息报头集合;后者则在服务端

37、实现对上下文SOAP报头的提取和当前上下文的设置。关于ClientMessageInspector和CallContextInitializer,本书的下一卷关于客户端和服务端处理流程,以及WCF扩展的部分,还将进行详细的介绍。自定义的ClientMessageInspector和CallContextInitializer定义在Infrastructures项目中,下面是相关代码实现:ContextSendInspector: 1: using System.ServiceModel; 2: using System.ServiceModel.Channels; 3: using Syste

38、m.ServiceModel.Dispatcher; 4: using System.Threading; 5: using Artech.PetShop.Common; 6: namespace Artech.PetShop.Infrastructures 7: 8: public class ContextSendInspector: IClientMessageInspector 9: 10: public void AfterReceiveReply(ref Message reply, object correlationState) 11: 12: 13: public objec

39、t BeforeSendRequest(ref Message request, IClientChannel channel) 14: 15: if (string.IsNullOrEmpty(ApplicationContext.Current.UserName) 16: 17: ApplicationContext.Current.UserName = Thread.CurrentPrincipal.Identity.Name; 18: 19: request.Headers.Add(new MessageHeader( 20: ApplicationContext.Current).G

40、etUntypedHeader( 21: ApplicationContext.ContextHeaderLocalName, ApplicationContext.ContextHeaderNamespace); 22: 23: return null; 24: 25: 26: ContextReceivalCallContextInitializer: 1: using System.ServiceModel; 2: using System.ServiceModel.Channels; 3: using System.ServiceModel.Dispatcher; 4: using A

41、rtech.PetShop.Common; 5: namespace Artech.PetShop.Infrastructures 6: 7: public class ContextReceivalCallContextInitializer : ICallContextInitializer 8: 9: public void AfterInvoke(object correlationState) 10: 11: ApplicationContext.Current.Clear(); 12: 13: 14: public object BeforeInvoke(InstanceConte

42、xt instanceContext, IClientChannel channel, Message message) 15: 16: ApplicationContext.Current = message.Headers.GetHeader(ApplicationContext.ContextHeaderLocalName, ApplicationContext.ContextHeaderNamespace); 17: return null; 18: 19: 20: 和应用大部分自定义扩展对象一样,上面自定义的ClientMessageInspector和CallContextInit

43、ializer可以通过相应的WCF行为(服务行为、终结点行为、契约行为或者操作行为)应用到WCF执行管道中。在这里我定义了一个行为类型:ContextPropagationBehaviorAttribute,它同时实现了IServiceBehavior和 IEndpointBehavior,所以既是一个服务行为,也是一个终结点行为。同时ContextPropagationBehaviorAttribute还继承自Attribute,所以可以通过特定的方式应用该行为。自定义ClientMessageInspector和CallContextInitializer分别通过ApplyClientBe

44、havior和ApplyDispatchBehavior方法应用到WCF客户端运行时和服务端运行时。ContextPropagationBehaviorAttribute定义如下: 1: using System; 2: using System.ServiceModel.Description; 3: using System.ServiceModel.Dispatcher; 4: namespace Artech.PetShop.Infrastructures 5: 6: public class ContextPropagationBehaviorAttribute:Attribute, IServiceBehavior,IEndpointBehavior 7: 8: #region IServiceBehavior Members 9: publicvoidAddBindingParameters(ServiceDescription ser

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论