在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
这两天在ERC的项目中重新设计用户验证,一开始已经实现了简单的利用ASP.NET的FORM验证实现了对用户登陆和安全检查的监测,但是由于牵涉到一个用户权限的分配和基于用户权限的UI显示,所以想对原有利用Session的读取方法加以改进,主要是考虑到Session的不稳定性。 首先想到的就是利用附加到Page.User的GenericPrincipal对象当中去,根据MSDN的帮助信息显示,可以看出GenericPrincipal对象是Microsoft设计的用于结合FORM验证对Active域或者IIS用户的权限读取的一个普通用户对象,当然我们可以手动将我们的数据附加到改对象中,并将改对象捆绑到Page.User对象中。经过漫长的调试,在昨天也就是周五基本实现了这个思路,具体代码如下 //构造人工身份证票据字符串 string m_UserData = Username.ToString() + "," + strRole.ToString() + "," + LastLoginIp.ToString() + "," + LastLoginTime.ToString(); //建立身份证票据对象,对象名为"Ticket",内置名"ERCUser",过期时间20分钟,包含用户名、用户权限、用户最后登陆IP,用户最后登陆时间信息 FormsAuthenticationTicket Ticket = new FormsAuthenticationTicket( 1,"ERCUser",DateTime.Now,DateTime.Now.AddMinutes(20),false,m_UserData,"/"); //加密序列化验证票为字符串 string EncryptTicket = FormsAuthentication.Encrypt(Ticket); //生成 //Http ERC = new Http(FormsAuthentication.FormsName,EncryptTicket); Http ERC = new Http("WebbUser",EncryptTicket); //将添加到Context上下文中 HttpContext.Current.Response.s.Add(ERC); Response.s["WebbUser"].Expires = DateTime.Now.AddMinutes(20); 由于我是在CallBack机制中实现的,所以没有调用页面回传,因此下面这条语句在不是无刷新机制的情况下可能需要调用 //Context.Response.Redirect(Context.Request["ReturnUrl"]); 上述代码我们构建了一个通过由ASP.NET的FormAuthenticaion加密的对象,在调试过程中,原本是使用FormsAuthentication.FormsName来作为这个的名字来存储(不知道FormsAuthentication.FormSName是什么?去翻MSDN,这个是要打PP的),但是经过N遍的调试,我才发现导致我在Global.asax中读取信息的就是因为不该使用了系统默认的名字,这个教训非常惨痛,经过N遍的调试,我才将错误的原因定位在此,后来在跟踪调试的时候发现,只要我的WEB页面一刷新,系统默认名字的信息就会改变,为什么呢?我还不知道呢。。。后面我修改使用WebbUser来作名字,就可以正常读取加密的信息了。 之后我们就可以在Global.asax的Application_AuthenticateRequest的事件中来处理了,实现代码如下: //在Global.asax中将该信息添加到服务端表示用户身份的GenericPrincipal对象中 Http ERC = Ctx.Request.s[FormsAuthentication.FormsName]; Http ERC = Ctx.Request.s["WebbUser"]; FormsIdentity Id = (FormsIdentity)Ctx.User.Identity ; FormsAuthenticationTicket Ticket = null; //取得身份验证票 try { Ticket = FormsAuthentication.Decrypt(ERC.Value); } catch(Exception ex) { // Log exception details (omitted for simplicity) return; } if (null == Ticket) { // failed to decrypt. return; } string[] Roles = Ticket.UserData.Split (',') ; //将身份验证票中的role数据转成字符串数组 Ctx.User = new GenericPrincipal (Id, Roles) ; //将原有的Identity加上角色信息新建一个GenericPrincipal表示当前用户,这样当前用户就拥有了role信息 这样,前面你构造的m_UserData字符串也就被添加到了Page.User的GennericPrincial对象当中了,虽然成功的添加了该字符串,最后在跟踪调试的时候发现User.Identity的GennericPrincial对象中确实有了一个Collecation的对象,里面包含了m_UserData的数据,但是我却无法将它们读取出来,因为在MSDN对GennericPrincial对象的解释中,只有一个InRole()的方法,只提供对所有用户角色的(该角色表示Active域或者Windows用户角色)检测,后面本来想利用事件反射机制来读取,不过最终以失败告终,因为我不知道应该使用那个方法来反射。。。。真是失败啊。。其代码如下: IIdentity identity = User.Identity; MethodInfo method = identity.GetType().GetMethod("IsInRole",BindingFlags.Instance | BindingFlags.NonPublic); string[] roleNames =(string[])method.Invoke(identity,new object[]{}); bool dd = User.IsInRole("test"); 系统编译出错,具体错误信息不记得了....-_- 通过一天一夜的折腾,最终改进FORM验证的思路夭折了,痛哭~~不过通过调试,更加进一步了解了FORM验证的机制和页面加载处理的顺序:(这里不能画图,有机会再画个示意图贴上得了) 既然附件权限数据到User.Identity中失败,只能采用另外的方法了,当然,也有人可能提出,可以自己写IPrincial接口来重载对User对象的控制,那样我觉得更复杂了,对偶的工作量还是没有减轻,既然又懒得写接口(应该是不会写),只能写PAGE基类来控制Session了,写一个BasePage.cs的基类,从System.Web.Ui.Page继承,重载Init()事件,在事件中检测Session是否存在,不存在则重新从数据库中读取并重置,这个过程当然得控制在用户通过票据验证,以下是具体代码; #region 事件处理 protected override void OnInit(EventArgs e) { base.OnInit(e); //作登陆后Session检测,防止Session稳定性失败导致应用程序崩溃 if(User.Identity.IsAuthenticated) { if(Session["Role"] == null || Session["UserName"] == null) { Member member = new Member(); SqlDataReader sdr_member = member.GetUserInfo(User.Identity.Name.ToString()); while(sdr_member.Read()) { Session["Role"] = sdr_member["Role"].ToString(); Session["UserName"] = sdr_member["Username"].ToString(); } } } this.Error += new System.EventHandler(this.PageBase_Error); } |
请发表评论