在ASP.NET AJAX中使用应用程序服务和本地化(2):示例程序:实现用户登录和注销

本文来自《ASP.NET AJAX程序设计 第II卷:客户端Microsoft AJAX Library相关》的第五章《应用程序服务和本地化》。

 

让我们通过编写一个完整的示例程序学习如何配置并使用ASP.NET AJAX的AuthenticationService对象来实现用户登录和注销。

5.2.1 启用身份认证应用程序服务

若想在我们的Web应用程序中使用ASP.NET 2.0内建的身份认证服务,首先就是在web.config文件中启用该服务。在web.config文件的<configuration />\<system.web />节下添加如下一段配置:

<authentication mode="Forms">
  <forms cookieless="UseCookies" loginUrl="~/AuthenticationServiceDemo.aspx"/>
</authentication>

这样即为该Web站点启用了身份认证应用程序服务,且设置认证的类型为Forms,即表单认证。loginUrl设置了Web站点的登录页面,这里的~/AuthenticationServiceDemo.aspx就是本示例程序的页面名称,也就是说,若用户的权限不够访问网站内的某个页面,ASP.NET会自动将用户重定向。

参考:更多有关ASP.NET 2.0内建的身份认证服务的内容,请参考这篇MSDN文章:《Authentication in ASP.NET: .NET Security Guidance》(http://msdn2.microsoft.com/en-us/library/ms978378.aspx)。

若想在客户端JavaScript脚本中直接使用AuthenticationService对象实现用户登录和注销,我们还需要对ASP.NET AJAX进行配置。同样是在web.config文件中,在<configuration />节下添加如下配置:

<system.web.extensions>
  <scripting>
    <webServices>
      <authenticationService enabled="true" />
    </webServices>
  </scripting>
</system.web.extensions>

其中将<authenticationService />节中的enabled属性设置为true之后,即可在客户端脚本中直接访问到ASP.NET 2.0的身份认证服务。

提示:更多有关ASP.NET AJAX的配置的介绍,请参考本书第I卷。

 

5.2.2 配置Membership数据库并创建用户

在ASP.NET 2.0内建的身份认证服务中,用户信息存放在Membership数据库中。在本机的machine.config配置文件(默认位置为C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\CONFIG\machine.config)中,我们可以找到如下一段:

<connectionStrings>
  <add name="LocalSqlServer" connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|aspnetdb.mdf;User Instance=true" providerName="System.Data.SqlClient" />
</connectionStrings>

若你希望使用自定义的数据库存放用户信息,可以在本Web站点的web.config文件中<connectionStrings />一节添加相应的配置元素。这里我们出于演示的目的,将使用该默认数据库。

参考:若想了解更多有关Membership数据库的相关内容,请参考这篇MSDN文章:《Configuring an ASP.NET Application to Use Membership》(http://msdn2.microsoft.com/en-us/library/6e9y4s5t.aspx)。

然后,在Visual Studio中选择“Website | ASP.NET Configuration”菜单项,打开ASP.NET Web Site Administration Tool工具,初始界面将如图5-1所示。

图5-1 ASP.NET Web Site Administration Tool的初始界面

在该工具的“Security”选项卡中,我们来为本Web站点添加一个用户,便于稍后使用该用户测试示例程序的登录/注销功能。这里我们创建的用户的用户名为“Dflying”,密码为“p@ssword”。如图5-2所示。

图5-2 使用ASP.NET Web Site Administration Tool创建用户

创建之后,Membership数据库中就包含了一个名为Dflying,密码为p@ssword的用户。请牢记上述登录信息,稍后即将用到。

参考:若想了解使用ASP.NET Web Site Administration Tool创建用户的详细步骤,请参考这篇MSDN文章:《Walkthrough: Creating a Web Site with Membership and User Login (Visual Studio) 》(http://msdn2.microsoft.com/en-us/library/879kf95c(VS.80).aspx)。

参考:ASP.NET Web Site Administration Tool还提供了很多非常有用的管理ASP.NET站点的功能,请参考这篇MSDN文章:《Web Site Administration Tool Overview 》(http://msdn2.microsoft.com/en-us/library/yy40ytx0.aspx)。

 

5.2.3 创建受保护的目录

为了区分登录用户和匿名用户的差别,我们接下来在该Web站点中创建一个受保护的、其中内容只能被登录用户访问的目录(名为SecuredFolder),并在其中创建一张ASP.NET页面(名为Default.aspx)和相应的web.config文件。图5-3显示了该目录在Visual Studio中的结构。

图5-3 Visual Studio中SecuredFolder目录的结构

为了在阻止匿名用户访问该目录中页面的同时,允许登录用户访问,我们需要按照如下所示修改该目中的web.config文件,注意其中粗体部分:  

<configuration>
  <appSettings/>
  <connectionStrings/>
  <system.web>
    <authorization>
      <deny users="?"/>
      <allow users="*"/>
    </authorization>
  </system.web>
</configuration>

而受保护的Default.aspx页面则非常简单,仅仅显示出一条说明信息而已:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Secured Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            Only authenticated users have access to this page.
        </div>
    </form>
</body>
</html>

上述配置之后,只有登录用户才能够访问到SecuredFolder/Default.aspx页面,匿名用户若尝试访问的话,将被重定向至~/AuthenticationServiceDemo.aspx页面。

 

5.2.4 示例程序的UI部分

完成了前面3小节的准备工作之后,我们终于能够开始编写示例程序了。新建一张名为AuthenticationServiceDemo.aspx的ASP.NET页面,让后让我们从程序的UI部分入手——当然,ScriptManager控件总是必不可少的:

<asp:ScriptManager ID="sm" runat="server" /> 

然后创建一个登录表单,该表单将显示给匿名用户。其中包含两个文本框,分别用来输入用户名和密码,并提供了一个按钮,用来触发用户登录操作。请注意登录表单和用户名/密码文本框的id,将在稍后的程序中用到:

<div id="loginPanel">
    <strong>Please Login</strong>
    <hr />
    <label for="tbUserName">User Name:</label>
    <input id="tbUserName" type="text" /><br />
    <label for="tbUserName">Password:</label>
    <input id="tbPassword" type="password" /><br />
<input id="btnLogin" type="button" value="Login" 
onclick="return btnLogin_onclick()" />
</div>

已登录用户不会看到上述表单,而会看到如下的注销面板。其中包含一个按钮,用来触发注销操作。同样要留意该面板的id:

<div id="logoutPanel">
    <strong>Welcome!</strong>
    <hr />
<input id="btnLogout" type="button" value="Logout" 
onclick="return btnLogout_onclick()" />
</div>

接下来添加一个<div />,将用来输出验证过程中的一些信息,包括可能发生的异常:

<div id="result"> 
</div> 

最后添加一个超链接,指向前一小节中创建的受保护目录。已登录用户和匿名用户都可以尝试访问该受保护目录,借此区分二者不同的权限:

<span>
    Authenticated users have access to this folder: 
    <a href="SecuredFolder/">SecuredFolder/</a>
</span>

这样即完成了本示例程序的UI部分。在Visual Studio的设计器中,完成后的程序界面将如图5-4所示。

图5-4 Visual Studio设计器中示例程序的界面

 

5.2.5 示例程序的JavaScript代码部分

完成了程序的UI部分之后,我们来用JavaScript代码把各个UI元素协调、统一起来,最终完成本示例程序。在页面中ScriptManager的出现位置之后添加一个<script />标签,接下来所有的脚本内容均将置于该标签中:

<script type="text/javascript">
</script>

首先编写pageLoad()函数,该函数将在客户端应用程序加载完毕后被ASP.NET AJAX客户端框架自动调用:

function pageLoad(sender, args) {
    // 刷新登录面板和已登录用户面板的显示状态
    refreshLoginPanels();
}

在上述pageLoad()函数中,我们调用了另外一个名为refreshLoginPanels()的函数。该函数用来根据当前用户是否已经登录,决定页面中应该显示出两个面板(登录表单和注销面板)中的哪一个。refreshLoginPanels()的函数的代码如下,注意其中使用了AuthenticationService对象的isLoggedIn属性:

function refreshLoginPanels() {
    // 用户是否已经登录?
    var isLoggedIn = Sys.Services.AuthenticationService.get_isLoggedIn();
    
    // 刷新登录面板和已登录用户面板的显示状态
    $get("loginPanel").style.display = isLoggedIn ? "none" : "block";
    $get("logoutPanel").style.display = !isLoggedIn ? "none" : "block";
}

接下来设定一下AuthenticationService对象的3个默认回调函数,这3个回调函数分别将在登录完成、注销完成以及调用失败时被调用:

Sys.Services.AuthenticationService.set_defaultLoginCompletedCallback(onLoginCompleted);
Sys.Services.AuthenticationService.set_defaultLogoutCompletedCallback(onLogoutCompleted);
Sys.Services.AuthenticationService.set_defaultFailedCallback(onAuthenticationFailed);

让我们依次分析这3个回调函数的实现。首先是登录完成的回调函数——onLoginCompleted()。其中先根据用户是否成功登录来显示出一段提示信息,然后调用前面给出过的refreshLoginPanels()函数更新两个面板的显示状态:

function onLoginCompleted(validCredentials, userContext, methodName) {
    // 根据用户是否成功登录,显示提示信息
    $get("result").innerHTML = 
        validCredentials ? "Logged in!" : "Incorrect UserName/Password!";
    
    // 刷新登录面板和已登录用户面板的显示状态
    refreshLoginPanels();
}

注销完成的回调函数——onLogoutCompleted()与前者非常类似。同样是先显示出提示信息,然后更新两个面板的显示状态:

function onLogoutCompleted(result, userContext, methodName) {
    // 显示用户注销信息
    $get("result").innerHTML = "Logged out!"
    
    // 刷新登录面板和已登录用户面板的显示状态
    refreshLoginPanels();
}

调用失败时的回调函数——onAuthenticationFailured()的篇幅较长,不过逻辑上却非常简单——取得异常的详细信息并显示给用户而已:

function onAuthenticationFailed(error, userContext, methodName) {
    // 取得异常信息
    var stackTrace = error.get_stackTrace();
    var message = error.get_message();
    var statusCode = error.get_statusCode();
    var exceptionType = error.get_exceptionType();
    var timeout = error.get_timedOut();
   
    // 显示异常信息
    $get("result").innerHTML = 
        "<strong>Error Occurs: </strong><br/>" +
        "<strong>Stack Trace: </strong>" +  stackTrace + "<br/>" +
        "<strong>Service Error: </strong>" + message + "<br/>" +
        "<strong>Status Code: </strong>" + statusCode + "<br/>" +
        "<strong>Exception Type: </strong>" + exceptionType + "<br/>" +
        "<strong>Is Timeout: </strong>" + timeout;
}

提示:出于演示的目的,本程序将异常的所有细节都毫无保留地显示了出来。而在实际应用程序中,我们完全没有必要如此“诚实”地将把一切都告知给用户。

“Login”按钮的click事件处理函数的代码如下。该事件处理函数中只是简单地调用了AuthenticationService对象的login()方法,并将用户名和密码传递给服务器。注意第三个参数表示用户的登录信息将以Cookie的形式保存下来,即使关闭后重新打开一个浏览器窗口,同样也可以得到上次的登录信息:

function btnLogin_onclick() {
    Sys.Services.AuthenticationService.login(
        $get("tbUserName").value, 
        $get("tbPassword").value,
        true
    );
}

“Logout”按钮的click事件处理函数的代码如下,其中调用了AuthenticationService对象的logout()方法,用来注销当前的用户:

function btnLogout_onclick() {
    Sys.Services.AuthenticationService.logout();
}

由于我们在前面已经设定了AuthenticationService对象的3个默认回调函数,所以在调用login()和logout()方法返回之后,AuthenticationService对象将自动根据当前情况选择并调用上述3个回调函数中的某一个。

到此为止,我们也完成了整个示例程序的编写。

 

5.2.6 运行示例程序

运行该示例程序,其初始界面将如图5-5所示。这时若是直接访问受保护的SecuredFolder目录,页面将被重定向回当前页面。

图5-5 示例程序的初始界面

输入用户名Dflying,密码p@ssword登录,可以看到程序即可以Ajax方式进行异步登录。登录后的界面如图5-6所示,注意登录表单被隐藏,显示出了注销面板。此时若是点击“Logout”按钮,当前的用户将被注销,程序也将返回至图5-5所示的界面。

图5-6 登录后的程序界面

此时再次访问受保护的SecuredFolder目录中的Default.aspx页面,即可浏览到该页面的内容,如图5-7所示。

图5-7 登录用户可以访问受保护的SecuredFolder目录中的Default.aspx页面

若是在进行身份认证时程序抛出了异常,我们也会看到异常的详细信息。比如,修改一下“Login”按钮的click事件处理函数的代码,将login()方法的第三个参数注释掉:

function btnLogin_onclick() {
    Sys.Services.AuthenticationService.login(
        $get("tbUserName").value, 
        $get("tbPassword").value//,
        // true
    );
}

再次尝试登录,我们将看到程序显示出如图5-8所示的异常信息。

图5-8 显示身份认证时程序抛出的异常

通过这样一个完整的示例程序,我们了解了使用ASP.NET AJAX的AuthenticationService对象实现Ajax方式的用户登录和注销的方法,若合理应用的话,定会为我们的Ajax应用程序增色不少,给用户登录/注销这一常用场景带来更加流畅的用户体验。

posted on 2007-07-12 08:12  Dflying Chen  阅读(12067)  评论(45编辑  收藏  举报