由Atlas无刷新登录到LoginView源码分析

前天做了个用Atlas的UpdatePanel包装LoginView的登录例子,代码骨架如下:

<asp:Panel runat="server" CssClass="modalPopup" style="display:none">
 
<atlas:UpdatePanel runat="server">
 
<asp:LoginView runat="server">
     
<AnonymousTemplate>
 //自己的登录UI和Login按钮处理
 
</AnonymousTemplate>
     
<LoggedInTemplate>
          您好!
<asp:LoginName runat="server" />
 
</LoggedInTemplate>
 
</ContentTemplate>
</atlas:UpdatePanel>
登录处理代码:
protected void LoginButton_Click(object sender, EventArgs e)
    
{
        
if(Page.IsValid){
            TextBox userName 
= this.LoginView1.FindControl("UserName"as  TextBox;
            TextBox password 
= this.LoginView1.FindControl("Password"as  TextBox;
            
bool isAuthenticated = LoginUtil.GetProvider("SQLMembershipProvider").ValidateUser(userName.Text, password.Text);
            
if (isAuthenticated)
            
{
                
                CheckBox remMe 
= this.LoginView1.FindControl("RememberMe"as CheckBox;
                FormsAuthentication.SetAuthCookie(userName.Text, remMe.Checked);
            }

            
else
            
{
                
//FormsAuthentication.RedirectToLoginPage("loginfailure=1");
                Literal failure = this.LoginView1.FindControl("FailureText"as Literal;
                failure.Text 
= "登录错误!";
            }

        }
        
    }

首先,我要说几点:
第一,切不能在<AnonymouseTemplate>里面加Login控件来妄想无刷新回调,我看了Login控件的源代码,它的SignOn处理中调用了Redirect方法,重定向浏览器你还能无刷么?所以,嘿嘿!!!要无刷,还得自给自足,自行写Login UI和登录代码。同样,想无刷注销也别用LoginStatus,它的处理也调用了Redirect.
但是尽管这样,我写的代码运行后,却出现了一个很奇怪的现象,在我输入正确信息后第一次点登录时,数据被提交,输入框数据也被清空了,Anonymous却没有转到LoggedIn。胡乱再输入一堆错误的用户名和密码后再点击一次登录,LoginView的模板从
Anonymous转到LoggedIn.
这是怎么回事呢?为什么第一次异步提交后模板没有被切换?为什么第二次提交错误的信息后,模板居然改变成LoggedIn了?

    嘿嘿,如果你不去看微软自家的实现,你可能一辈子都不知道为什么。于是,隔日我又反编译之,获取LoginView源码,来解读一番,顿知晓其究竟。
首先,请你看看模版是根据什么切换的,
大家内行都知道,ASP.NET控件生成HTML有2步:
OnPreRender -> Render
那么,请各位来看看LoginView中的这两个方法:

protected internal override void OnPreRender(EventArgs e) {
     
base.OnPreRender(e);
     
//预render时调用GetTemplateIndex()来获取相应模板
     this.TemplateIndex = this.GetTemplateIndex();
 
this.EnsureChildControls();
}

protected internal override void Render(HtmlTextWriter writer) {
     
this.EnsureChildControls();
 
base.Render(writer);
}

OnPreRender中调用了GetTemplateIndex()来获取模板索引, 不用我说,呆会真正Render的时候,肯定是根据TemplateIndex来选择是用Anonymous还是LoggedIn还是RoleGroup模板了。所以关键就是看GetTemplate是根据什么条件来选择模板的。(Render就不用看了,只是调用了一个EnsureChildControls和几基类的Render, 显然没有什么名堂).
接下来,请大家来看看我们的GetTemplateIndex方法了,如下:
//获得模板索引
private int GetTemplateIndex() {
     
//根据请求是否被认证来返回相应的模板
     if ((base.DesignMode || (this.Page == null)) || !this.Page.Request.IsAuthenticated)
 
{
      
return 0;
     }

     IPrincipal principal1 
= LoginUtil.GetUser(this);
     
int num1 = -1;
     
if (principal1 != null)
 
{
      num1 
= this.RoleGroups.GetMatchingRoleGroupInternal(principal1);
     }

     
if (num1 >= 0)
 
{
      
return num1 + 2;
     }

 
return 1;
}

值得注意的是,第三行中的!this.Page.Request.IsAuthenticated, 它是根据Request对象IsAuthenticated的值来判断该返回相应模板的索引。
显然,我之前的代码,在第一次登录时,发送过去的Request对象的IsAuthenticated属性的值显然为false( Request.IsAuthenticated返回的是HttpContext.Current.IsAuthenticated),因为还没有登录成功被认证,并且,我做过测试,在FormsAuthentication.SetAuthCookie(userName.Text, remMe.Checked);之后, Trace Request.IsAuthenticated的值,仍为false. 所以这就导致第一次提交登录后并没有切换模板到LoggedIn, 那为什么第二次胡乱输入切又会改变呢?这很简单,当我们第二次提交登录时,尽管用户名和密码可能都是错误的,但是,这时的Request的IsAuthenticated属性已经是true了,所以第二次的提交当然会改成LoggedIn了。
如果你细心,你可能会问,既然第一次Request.IsAuthenticated为false, 并且GetTemplateIndex()是通过Request.IsAuthenticated来选择模板,那岂非传统的Login亦如此,需两次登录Click方可。这里,你先清楚一点,不管是atlas封装的无刷新调用也好,还是传统的Login控件,他们在登录处理时都调用了LoginView的Render,这显然了,否则在altas下n次也转换不了模板。 那么既然都是调用了,为何结果不同呢?呵呵,这就得看HttpContext.Current.IsAuthenticated何时变为true,此番待下回分解。
posted on 2006-10-21 05:56  彭帅  阅读(3206)  评论(2编辑  收藏  举报