官术网_书友最值得收藏!

  • 決戰.NET
  • 黃忠成
  • 2764字
  • 2018-12-27 16:56:02

4.4 AuthenticationService

ASP.NET AJAX內建了兩個關于使用者的Service:AuthenticationService及ProfileService,AuthenticationService是用來驗證使用者的,它與ASP.NET既有的使用者管理機制結合,提供了以JavaScript來驗證使用者的途徑。嗯!聽來似乎不錯,那何時會需要用JavaScript,而非傳統的ASP.NET Login系列控件來驗證用戶呢?嗯,很有趣的問題,筆者能想到的情況只有一種,就是制作類似Yahoo、PCHome等門戶網站時,首頁上會放上一個區塊供使用者登錄網站用,在使用者登錄網站后,首頁上會添加一些只有已登錄者才能使用的功能,如修改用戶密碼等,這原本并不難,只要利用ASP.NET所提供的Login控件便可輕易達到,難的是在原來的首頁上本來就有很多內容,如果想使用者登錄后不產生網頁刷新的動作,那就比較麻煩了。此時單單使用Login控件是很難達到此需求的,即使將Login控件放在UpdatePanel控件內,也不能阻止其內建的行為:登錄后轉向特定網頁!此時便是使用AuthenticationService的最佳時機了。請照著以下步驟做。

1. 建立一個新Web Site,命名為AuthenticationTest。

2. 配置此Web Site的使用者認證機制,并添加使用者。

3. 新建一個文件夾:SecureFolder。

4. 在SecureFolder中加入一個新網頁:SecureForm.aspx,內容如程序4-11所示。

5. 在Web Site的根目錄下建立一個UserControl:LoginArea.ascx,內容如程序4-12所示。

6. 在Web Site的根目錄下建立一個JavaScript程序文件:JScript.js,內容如程序4-13所示。

7. 修改web.config中的authentication區段,如程序4-14所示。

8. 在Default.aspx中加入ScriptManager控件。

9. 在Default.aspx中放入LoginArea這個UserControl。

程序4-11

    Samples\4\AuthenticationTest\SecureFolder\SecureForm.aspx
    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="SecureForm.aspx.cs"
      Inherits="SecureFolder_SecureForm" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
          <form id="form1" runat="server">
          <div>
            &nbsp;<asp:Label ID="Label1" runat="server" Text= "i am secure
              Form"></asp:Label>
        </div>
        </form>
    </body>
    </html>

程序4-12

    Samples\4\AuthenticationTest\LoginArea.ascx
    <%@ Control Language="C#" AutoEventWireup="true" CodeFile="LoginArea.ascx.cs"
      Inherits="LoginArea" %>
    <table border="0" cellpadding="0" cellspacing="0">
    <tr>
    <td style="width: 57px"><span id="lbAccount">賬號:</span></td>
    <td>
        <input type="text" id="txtUserName" />
        </td>
    </tr>
    <tr>
    <td style="width: 57px"><span id="lbPassword">密碼:</span></td>
    <td>
        <input type="password" id="txtPassword" />
        </td>
    </tr>
    <tr>
    <td style="width: 57px"></td>
    <td>
        <input id="btnLogin" type="button" value="登錄" onclick="OnClickLogin()" />
        <span id="lbErrorInfo"></span></td>
    </tr>
    </table>

程序4-13

    Samples\4\AuthenticationTest\JScript.js
    // JScript File
    //聲明儲存HTML控件的變量
    var username;
    var password;
    var buttonLogin;
    var errorInfo;
    var lbUserName;
    var lbPassword;
    //pageLoad函數是ASP.NET AJAX Client Library在網頁載入時默認會調用的函數
    function pageLoad()
    {
        //取得用戶名稱的Text控件之HTML對象
        username = $get("txtUserName");
        //取得密碼的Text控件之HTML對象
        password = $get("txtPassword");
        //取得登錄按鈕的HTML對象
        buttonLogin = $get("btnLogin");
        //取得顯示錯誤用的HTML對象
        errorInfo = $get("lbErrorInfo");
        //取得用戶名稱之提示HTML對象
        lbUserName = $get("lbAccount");
        //取得密碼之提示HTML對象
        lbPassword = $get("lbPassword");
    }
    //于注銷時調用
    function OnClickLogout()
    {
        //調用此函數,可以將現行用戶注銷
        Sys.Services.AuthenticationService.logout(null,
            null, null, null);
    }
    //失敗時調用的函數
    function OnFailed(error,
          userContext, methodName)
    {
          errorInfo.innerText = error.get_message();
    }
    //登錄驗證完畢時調用的函數
    function OnLoginCompleted(validCredentials,
          userContext, methodName)
    {
        password.value = "";
        if (validCredentials == true)
        {
            username.value = "";
            password.value = "";
            username.style.visibility = "hidden";
            password.style.visibility = "hidden";
            lbUserName.style.visibility = "hidden";
            lbPassword.style.visibility = "hidden";
            // Hide login fields.
            buttonLogin.value = "注銷";
            errorInfo.innerText = "";
        }
        else
        {
            errorInfo.innerText = "登錄失敗";
            buttonLogin.value = "登錄";
        }
    }
    //注銷完畢后調用的函數
    function OnLogoutCompleted(result)
    {
        buttonLogin.value = "注銷";
        username.style.visibility = "visible";
        password.style.visibility = "visible";
        lbUserName.style.visibility = "visible";
        lbPassword.style.visibility = "visible";
    }
    function OnClickLogin()
    {
        Sys.Services.AuthenticationService.set_defaultLoginCompletedCallback(
          OnLoginCompleted);
        Sys.Services.AuthenticationService.set_defaultLogoutCompletedCallback(
          OnLogoutCompleted);
        Sys.Services.AuthenticationService.set_defaultFailedCallback(OnFailed);
        if(buttonLogin.value == "注銷")
          OnClickLogout();
        Sys.Services.AuthenticationService.login(username.value,
            password.value, false,null,null,null,null,"User Context");
    }

程序4-14

    Samples\4\AuthenticationTest\web.config
    <system.web>
    ...............
    <authentication mode="Forms">
          <forms loginUrl ="Default.aspx"/>
    </authentication>
    </system.web>
    <system.web.extensions>
                  <scripting>
                          <webServices>
                      <authenticationService enabled="true" />
                          </webServices>
                  </scripting>
        </system.web.extensions>

這個范例是較復雜的,首先請看程序4-14 的web.config片段,ASP.NET AJAX的Authentication Service默認是不加載的,要使用這個Service必須先在web.config中將其設為自動加載,也就是將authenticationService區段的enabled設為True。接著便是準備登錄所使用的UI界面,本例將UI界面設計成UserControl,方便套用至任何網頁中,當使用者點擊登錄按鈕后,位于程序4-13中的OnClickLogin函數便會被調用,該函數一開始是通過Authentication Service所提供的JavaScript對象:Sys.Services.AuthenticationService中的3 個函數來設定調用Authentication Service時所需要的3個Callback函數,它們分別是:

1. 登錄驗證完畢時調用的函數,此處設定為OnLoginCompleted。

2. 注銷完畢時調用的函數,此處設定為OnLogoutCompleted。

3. 調用Authentication Service失敗時調用的函數,此處設定為OnFailed。

設定好這些Callback函數后,OnClickLogin函數會先判斷目前的狀態。若為已登錄,則執行注銷動作,若為未登錄則執行登錄動作,也就是調用Sys.Services.AuthenticationService.login函數進行該用戶的登錄程序,當登錄驗證完畢,OnLoginCompleted函數就會被調用,傳入的validCredentials參數代表著該使用者是否驗證通過,True表示此使用者已驗證通過,False則是驗證失敗,此處所做的只是調整UI界面的可視與不可視而已。在注銷部分,此例是調用了Sys.Services.AuthenticationService.logout函數來注銷現行使用者,此動作執行完畢OnLogoutCompleted函數就會被調用,此處同OnLoginCompleted一樣,只是調整UI界面的可視與不可視狀態。在這個例子中還有兩個部分尚未提及,一是pageLoad函數,在ASP.NET AJAX的網頁中,如將JavaScript的函數取名為pageLoad,代表著該函數會在網頁載入時、Async-Postback刷新完成后調用,這是由ASP.NET AJAX Client Framework所負責調用的,此例中的pageLoad函數以$get函數將所有相關的HTML對象取出并存在變量中,供后面的OnClickLogin等函數使用。第二個未提及的是錯誤的控管機制,在LoginArea.ascx中有著如程序4-15所示的HTML代碼。

程序4-15

    <span id="lbErrorINfo"></span>

這是用來顯示錯誤信息用的,當登錄驗證失敗,或者注銷失敗時,OnLoginCompleted或OnFailed函數會將錯誤信息設定給HTML對象來顯示。執行此程序后,會見到如圖4-6 所示的畫面。

true

圖4-6

輸入用戶及密碼,點擊登錄按鈕后便完成了登錄動作(本例是admin、admin@test),之后原本的登錄按鈕就會變成注銷,其他的UI控件便會變成不可視,這是在OnLoginCompleted函數中完成的,如圖4-7所示。

true

圖4-7

接著點擊Go Secure Form鏈接,便會進入只有登錄者才能瀏覽的網頁,如圖4-8所示。

true

圖4-8

若未登錄而企圖瀏覽SecureForm.aspx,將會被導回Default.aspx,這是ASP.NET使用者管理機制的默認行為。Authentication Service的特色是登錄及注銷時,都不需要任何的網頁刷新或是部分刷新動作,這滿足了前面提及的希望在不刷新網頁情況下完成登錄的需求。雖說如此,但是為了這件事而大費周章寫下一堆JavaScript程序代碼,似乎有點不計時間成本,難道就沒有比較簡單的方法達到同樣的效果嗎?事實上是有的,只要利用Login控件及UpdatePanel控件就可以達到同樣的效果,等會!剛剛不是說不行嗎?怎么這下又說行了?別誤會,我只是說不容易達到,因為Login控件在登錄成功后會導向特定網頁,這個動作會使UpdatePanel控件的部分刷新完全無用武之地,但是!如果可以讓Login控件在登錄成功后不導向其他網頁,那UpdatePanel的部分刷新便可以派上用場,這樣一來,登錄的動作便不會影響到整個網頁,而只是刷新Login控件所在的UpdatePanel控件而已。那具體的做法呢?請照以下的步驟做。

1. 建立一個新網頁,命名為Default2.aspx。

2. 加入一個ScriptManager控件。

3. 加入一個UpdatePanel控件。

4. 在UpdatePanel控件中加入一個HyperLink控件,NavigateUrl設為SecureForm.aspx。

5. 在UpdatePanel控件中加入一個Login控件,命名為Login1。

6. 在UpdatePanel控件中加入一個LinkButton控件,命名為LinkButton1,Text設為Logout,Visible設為False。

7. 在LinkButton1的Click事件中鍵入程序4-16中的LinkButton1_Click函數中的代碼。

8. 在Login1的LoggingIn事件中鍵入程序4-16中的Login1_LogginIn函數中的代碼。

程序4-16

    Samples\4\AuthenticationTest\Default2.aspx.cs
    using System;
    using System.Data;
    using System.Configuration;
    using System.Collections;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    public partial class Default2 : System.Web.UI.Page
    {
        private bool _customAuthed = false;
        protected void Page_Load(object sender, EventArgs e)
        {
        }
        protected void Page_PreRender(object sender, EventArgs e)
        {
            if (Membership.GetUser() != null)
            {
                Login1.Visible = true;
                LinkButton1.Visible = true;
            }
            else if(!_customAuthed)
            {
                Login1.Visible = true;
                LinkButton1.Visible = false;
            }
        }
        protected void Login1_LoggingIn(object sender, LoginCancelEventArgs e)
        {
            if (Membership.Provider.ValidateUser(Login1.UserName, Login1.Password))
            {
                FormsAuthentication.SetAuthCookie(Login1.UserName,
                  Login1.RememberMeSet);
                Login1.Visible = false;
                LinkButton1.Visible = true;
                _customAuthed = true;
                e.Cancel = true;
            }
        }
        protected void LinkButton1_Click(object sender, EventArgs e)
        {
            FormsAuthentication.SignOut();
        }
    }

在執行程序前,請先將web.config中的forms區段所設定的loginUrl改為Default2.aspx,完成后便可執行程序,如圖4-9所示。

true

圖4-9

輸入用戶及密碼后點擊Log In按鈕,此時便可看到Logout的聯結出現,再點擊Go Secure Form,即可瀏覽僅登錄者所能瀏覽的SecureForm.aspx,重點是!這個登錄動作只刷新了Login控件所在的UpdatePanel控件,不會影響到整個網頁,如圖4-10所示。

true

圖4-10

由程序可以看出,這個例子運用了Login控件所提供的自定義驗證用戶功能,以手動驗證使用者后告知Login控件的驗證動作已取消,阻止后續的網頁導向動作的方式。這種方式也不是沒有缺點,缺點是LoginName、LoginStatus等控件無法在第一次登錄時反應過來,致使出現不正常的狀態,所以此例才未使用這些控件,改用LinkButton控件來提供Logout功能。那Authentication Service與這個方法哪種較好呢?個人認為,Authentication Service的難用之處在于若要從頭一個字一個字地建立JavaScipt程序代碼,感覺有點麻煩且難以除錯,但是只要有一個既有的Script樣板,也就是程序4-13的JScript.js后,日后只要針對使用的網頁來進行細微調整即可。第二種使用Login控件的方式雖然看起來很不錯,但或許會讓人感覺有點取巧,尤其是在性能上,使用UpdatePanel控件雖然可以部分刷新網頁,但這仍無法掩蓋其執行了一個Postback動作的事實,Server必須在Postback到達時重新建立網頁上的所有控件、解讀VeiwState,這些都是必須付出的代價。

主站蜘蛛池模板: 海伦市| 石屏县| 安徽省| 班戈县| 涿州市| 齐齐哈尔市| 武冈市| 子长县| 揭东县| 天台县| 曲阳县| 黄龙县| 常熟市| 玉龙| 商城县| 军事| 商城县| 务川| 勃利县| 泽库县| 徐闻县| 静安区| 荥阳市| 咸阳市| 郸城县| 二手房| 乐至县| 高邮市| 伊宁市| 武邑县| 涟水县| 芜湖县| 昌黎县| 阜平县| 萨迦县| 平乡县| 皮山县| 屏东县| 安宁市| 丰都县| 辰溪县|