一区二区久久-一区二区三区www-一区二区三区久久-一区二区三区久久精品-麻豆国产一区二区在线观看-麻豆国产视频

Asp.Net 用戶驗證(自定義IPrincipal和IIdentity)

源碼下載:http://www.tracefact.NET/SourceCode/FormsAuthentication.rar

引言

前一段時間有兩個朋友問我,為什么在HttpModule中無法獲得到Session值,因為他們希望自定義一個HttpModule,然后在其中獲取Session來進行用戶驗證。我奇怪為什么不使用.NETFramework已經(jīng)提供的驗證機制,而要和ASP時一樣,自己手工進行cookie+Session驗證?我們是基于.NETFramework這個平臺進行編程,所以我覺得,在很多情況下,使用Framework已經(jīng)建立好的機制會顯著地提高工作效率,而且.NETFramework內(nèi)置的驗證機制通常也更加安全。

.NET提供了一整套的驗證和授權(quán)機制,這里驗證和授權(quán)是不同的概念,驗證(Authentication)是指“證明你確實是你所說的人”,通常是提供一個用戶名和口令,然后與持久存儲(比如數(shù)據(jù)庫)中的用戶名和口令進行對比。授權(quán)(Authorization)是指“你是否有足夠的權(quán)限做某件事”,此時你的身份已經(jīng)被證明過了(匿名用戶、會員還是管理員),授權(quán)通常與用戶組或者用戶級別聯(lián)系起來,不同的用戶組擁有不同的權(quán)限(訪問特定頁面或者執(zhí)行特定操作)。

回想一下我剛接觸.NET時,也曾經(jīng)完全繞過.NET的驗證,自己編碼采用Cookie+Session實現(xiàn)身份驗證,并且一個ASP.NET 登錄控件都沒有使用,那時候的理由是:我要使用自定義的用戶表,不能使用ASP.NET安全機制在App_Data下自動生成的ASPNETDB.mdf中的一系列數(shù)據(jù)表。除此以外,還有一個原因,就是.NET驗證機制的核心IPrincipal和Identity提供的信息用戶信息太少了,當在頁面后置代碼中使用繼承來的User屬性(IPrincipal類型)時,它的Identity屬性只有一個Name與用戶數(shù)據(jù)相關(guān)(AuthenticationType與IsAuthenticated都是與驗證相關(guān)),而很多時候我們都需要許多額外的用戶數(shù)據(jù)。其實這只是一個誤解罷了,以為使用ASP.NET的驗證機制和登錄控件就一定要使用其附帶的數(shù)據(jù)表,以為Identity就只能攜帶一個Name屬性。

實際上,.NET的安全機制包括了幾個部分,除了驗證以外,還包括MemberShip、Profile、Role等,我們完全可以只使用它的驗證機制,而繞過它的MemberShip、Profile和Role,來實現(xiàn)通常我們用Cookie+Session完成的功能,而且更高效更安全。這篇文章將快速地實現(xiàn)這樣的一個流程。

開始前的準備

創(chuàng)建頁面,配置Web.config

我們先創(chuàng)建解決方案、建立站點,然后在站點中添加下述文件,它們將會在后面使用:

接著對Web.config進行一下配置,首先看根目錄下的Web.config:

<?xml version="1.0"?>
<configuration>
    <system.web>
        <authentication mode="Forms">
            <forms timeout="600" slidingExpiration="true" loginUrl="~/SignIn.ASPx" />
        </authentication>
    </system.web>
   
    <location path="AuthOnly.ASPx" >
        <system.web>
            <authorization>
                <deny users="?" />
            </authorization>
        </system.web>
    </location>
</configuration>

這里我們指定了采用Forms驗證,并且設(shè)置用戶身份驗證過期時間為600分鐘(默認為30分鐘),slidingExpiration的意思是說timeout采用絕對時間還是滑動時間,當采用滑動時間時,如果在timeout時間內(nèi)再次瀏覽頁面,用戶的最后活躍時間將設(shè)為當前時間,并重新開始計算,這里我們采用滑動時間。loginUrl指定了登錄頁面,當匿名用戶訪問需要驗證后才能訪問的頁面時,將會到自動導(dǎo)航到這里所設(shè)置的SignIn.ASPx頁面,默認為Login.ASPx。

接著我們指定AuthOnly.ASPx頁面為只有驗證過的用戶才可以訪問。然后創(chuàng)建了AuthOnly文件夾,在其下添加了一個web.config,對這個目錄進行設(shè)置,指定該文件夾下所有文件只允許驗證用戶進行訪問。

<configuration>
    <system.web>
         <authorization>
             <deny users="?" />
         </authorization>
    </system.web>
</configuration>

創(chuàng)建用戶數(shù)據(jù)表和數(shù)據(jù)訪問

既然是用戶登錄,所以我們自然需要一張用戶表,在App_Data下創(chuàng)建一個SiteData數(shù)據(jù)庫,然后添加一張User用戶表,表的設(shè)置如下:

這個表模擬了一個小型的論壇用戶表,字段的含義基本都是自解釋的,UserImage是用戶頭像的地址,PostCount是用戶的發(fā)帖數(shù),ReplyCount是用戶的回帖數(shù),Level是用戶的級別。我已經(jīng)為表中添加了兩條范例數(shù)據(jù),其中一條用戶名為JimmyZhang,密碼為password。

接下來我們需要添加一個存儲過程,這個存儲過程接收一個name參數(shù),和一個password輸出參數(shù),根據(jù)name判斷User表中是否存在該用戶,如果存在,則由password帶回正確的密碼:

ALTER PROCEDURE dbo.IsValidUser
(
    @userName varchar(50),
    @password varchar(50) OUTPUT
)
AS
    if Exists(Select Id From [User] Where [Name] = @userName)
        Begin
            Select @password = Password From [User] Where [name]= @userName
            Select 1        -- Ture
        End    
Select 0        -- false

這樣做的目的是為了程序能夠區(qū)分“不存在此用戶”和“用戶存在,但是密碼不正確”這兩種情況。如果Select的where子句為[name]=@userName and [password] =@password,則無法進行區(qū)分。由數(shù)據(jù)庫帶回了正確的密碼之后,我們只需要在程序中與用戶輸入的密碼進行對比就可以知道用戶的密碼是否正確。

接下來我們創(chuàng)建一個強類型DataSet作為我們的數(shù)據(jù)訪問層,因為我發(fā)現(xiàn)使用強類型DataSet作數(shù)據(jù)訪問是最快的,基本不需要編寫一行代碼,在App_Code中添加一個AuthDataSet數(shù)據(jù)集文件,然后將User表拖進去,另外配置一下UserTableAdapter,添加兩個方法,一個是GetUserTable(@name),它根據(jù)name參數(shù)獲得用戶信息;一個是IsValidUser(@userName,@password),它調(diào)用了上面的存儲過程,并且返回一個標量值(0或者1)。

配置好以后,你的AuthDataSet應(yīng)該和下面一樣:

如果你查看一下生成的IsValidUser()方法,就會發(fā)現(xiàn)它具有這樣的簽名:

public virtual object IsValidUser(string userName, ref string password)

由于它返回的是一個object類型,并且接收的是一個ref參數(shù),盡管這樣最通用,但是可能不夠方便,注意到UserTableAdapter是一個部分類,所以我們可以在App_Code中再創(chuàng)建一個UserTableAdapter部分類,對它進行一個簡單的包裝:

namespace AuthDataSetTableAdapters {

    // 檢查是否是正確的用戶名,如果是正確的用戶名,帶回正確的密碼
    public partial class UserTableAdapter {    
        public bool IsValidUserST(string userName, out string password) {
            password = "";
            return Convert.ToBoolean(this.IsValidUser(userName, ref password));        
        }
    }
}

這里的方法后綴ST,意思是StrongType(強類型)。好了,現(xiàn)在我們的數(shù)據(jù)訪問就已經(jīng)OK了,接下來我們看一下第一個頁面:SignIn.ASPx用戶登錄頁面。

用戶登錄 -- 為Identity添加用戶數(shù)據(jù)

Login.ASPx頁面實現(xiàn)

在登錄頁面,我們需要針對登錄用戶和非登錄用戶做不同的處理:如果用戶尚未登錄,則顯示登錄用的表單;如果用于已經(jīng)登錄了,則顯示登錄用戶名并進行提示。完成這件事最好就是使用LoginView控件和LoginName控件了:

<ASP:LoginView ID="LoginView1" runat="server">
    <LoggedInTemplate>
        <ASP:LoginName ID="LoginName1" runat="server" />
        ,你已經(jīng)登錄了^_^ <br /><br />
                 
        你可以選擇 <ASP:LoginStatus ID="LoginStatus1" runat="server" LogoutPageUrl="~/Logout.ASPx" LogoutAction="Redirect"  />
    </LoggedInTemplate>
    <AnonymousTemplate>
        用戶名:<ASP:TextBox ID="txtUserName" runat="server" Width="128px"></ASP:TextBox>
        <br />
        密 碼:<ASP:TextBox ID="txtPassword" runat="server"></ASP:TextBox>
        <br />
        <ASP:Button ID="btnLogin" runat="server" Text="登 錄" onclick="btnLogin_Click" Width="100px" />
        <br />
        <br />
        <ASP:Label ID="lbMessage" runat="server" ForeColor="Red" Text=""></ASP:Label>
    </AnonymousTemplate>
</ASP:LoginView>

這里的關(guān)鍵是“登錄”按鈕的代碼后置文件,在“引言”部分,我們提到了Identity中的信息太少,為了向Identity中添加信息,我們可以先獲得FormsIdentity的Ticket屬性,它是一個FormsAuthenticationTicket類型,它含有一個UserData字符串屬性可以用于承載我們的用戶數(shù)據(jù),遺憾的是這個屬性是只讀的,為了給這個屬性賦值,我們需要重新新構(gòu)建一個FormsAuthenticationTicket,并在構(gòu)造函數(shù)中傳入我們想要添加的用戶信息。FormasAuthenticationTicket包含了諸多用于用戶驗證的信息,它從Cookie中獲得,可以認為它是服務(wù)端對Cookie的一個包裝,只是這里的Cookie的操作不需要我們來處理,而由ASP.NET運行時去處理。具體的代碼如下:

public partial class SignIn : System.Web.UI.Page {

    private enum LoginResult {
        Success,
        UserNotExist,
        PasswordWrong
    }

    // 用戶登錄
    private LoginResult Login(string userName, string password) {

        string validPassword;   // 包含正確的密碼
        AuthDataSetTableAdapters.UserTableAdapter adapter =
            new AuthDataSetTableAdapters.UserTableAdapter();

        // 判斷用戶名是否正確
        if (adapter.IsValidUserST(userName, out validPassword)) {
            // 判斷密碼是否正確
            if (password.Equals(validPassword))
                return LoginResult.Success;
            else
                return LoginResult.PasswordWrong;
        }

        // 用戶名不存在
        return LoginResult.UserNotExist;
    }
   
    protected void btnLogin_Click(object sender, EventArgs e) {

        TextBox txtUserName = LoginView1.FindControl("txtUserName") as TextBox;
        TextBox txtPassword = LoginView1.FindControl("txtPassword") as TextBox;
        Label lbMessage = LoginView1.FindControl("lbMessage") as Label;

        string userName = txtUserName.Text;
        string password = txtPassword.Text;

        LoginResult result = Login(userName, password);

        string userData = "登錄時間" + DateTime.Now.ToString();

        if (result == LoginResult.Success) {
            SetUserDataAndRedirect(userName, userData);        
        } else if (result == LoginResult.UserNotExist) {
            lbMessage.Text = "用戶名不存在!";
        }else {
            lbMessage.Text = "密碼有誤!";
        }
    }


    // 添加自定義的值,然后導(dǎo)航到來到此頁面之前的位置
    private void SetUserDataAndRedirect(string userName, string userData) {
        // 獲得Cookie
        HttpCookie authCookie = FormsAuthentication.GetAuthCookie(userName, true);

        // 得到ticket憑據(jù)
        FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(authCookie.Value);

        // 根據(jù)之前的ticket憑據(jù)創(chuàng)建新ticket憑據(jù),然后加入自定義信息
        FormsAuthenticationTicket newTicket = new FormsAuthenticationTicket(
            ticket.Version, ticket.Name, ticket.IssueDate,
            ticket.Expiration, ticket.IsPersistent, userData);

        // 將新的Ticke轉(zhuǎn)變?yōu)镃ookie值,然后添加到Cookies集合中
        authCookie.Value = FormsAuthentication.Encrypt(newTicket);
        HttpContext.Current.Response.Cookies.Add(authCookie);

        // 獲得 來到登錄頁之前的頁面,即url中return參數(shù)的值
        string url = FormsAuthentication.GetRedirectUrl(userName, true);

        Response.Redirect(url);
    }
}

我們首先定義了一個枚舉,用來說明點擊登錄后的狀態(tài):Success(成功)、UserNotExsit(用戶不存在)以及PasswordWrong(用戶名存在,但密碼錯)。Login()方法調(diào)用了上一小節(jié)我們定義的強類型DataSet中的IsUserValidST()方法,然后返回登錄結(jié)果。“搜索”按鈕的事件處理方法反而非常簡單,如果登錄失敗時在頁面顯示失敗原因,如果登錄成功則調(diào)用SetUserDataAndRedirect()方法。

在SetUserDataAndRedirect()方法中,我們執(zhí)行了主要的邏輯,我們先獲得了ASP.NET用于驗證的Cookie,從Cookie中得到FormsAuthenticationTicket,最后,執(zhí)行了前面所敘述的步驟,將我們自定義的數(shù)據(jù) --當前用戶的登錄時間記錄到了一個新構(gòu)建的FormsAuthenticationTicket中,最后將它進行編碼然后賦值給Cookie。接著我們導(dǎo)航到了來到SignIn.ASPx之前所在的頁面。

Default.ASPx 頁面預(yù)覽

默認情況下SignIn.ASPx在登錄成功后會導(dǎo)航到Default.ASPx頁面,所以我們先簡單的構(gòu)建一下Default.ASPx頁面,看看實現(xiàn)的效果:

<ASP:LoginView ID="LoginView1" runat="server">
    <AnonymousTemplate>
        歡迎訪問, 游客 !     
    </AnonymousTemplate>
    <LoggedInTemplate>
        你好, <ASP:LoginName ID="LoginName1" runat="server" /> ! <br />
        <strong>UserData值:</strong>
        <ASP:Literal ID="lbUserData" runat="server" />
    </LoggedInTemplate>
</ASP:LoginView>
<br />
<ASP:LoginStatus ID="LoginStatus1" runat="server" LogoutPageUrl="~/Logout.ASPx" LogoutAction="Redirect"  />

類似地,我們放置了一個LoginView控件,只是這里我們多放置了一個LoginStatus控件。接下來我們看一下后置代碼:

protected void Page_Load(object sender, EventArgs e) {

    if (!IsPostBack) {
        if (Request.IsAuthenticated) {
            FormsIdentity identity = User.Identity as FormsIdentity;
            string userData = identity.Ticket.UserData;
            Literal lbUserData = LoginView1.FindControl("lbUserData") as Literal;
            lbUserData.Text = userData;
        }
    }
}

最后我們先進行登錄,然后再打開Default.ASPx頁面,會看到類似這樣的輸出:

至此,我們已經(jīng)看到了如何利用FormsAuthentionTicket來附帶額外的用戶數(shù)據(jù),但是我們應(yīng)該看到這種做法存在的問題:可以保存的數(shù)據(jù)過于單一,僅僅只是一個字符串。而我們第一節(jié)中所介紹的用戶表包括各種類型的各種數(shù)據(jù)。如果你看過了 從一個范例看XML的應(yīng)用 這篇文章,你應(yīng)該立刻想到此處又是一個“單一字符串保存多種不同類型數(shù)據(jù)”的應(yīng)用場景,我們可以定義XML來解決。對于這種方式,我不再演示了。實際上,我們可以自定義一個IPrincipal和IIdentity來完成,接下來就來看一下。

自定義IPrincipal和IIdentity

不管是在Windows上還是在Web上,.NET都使用這兩個接口來實現(xiàn)用戶的身份驗證。它們不過是一個接口,實現(xiàn)了這兩個接口的類型附帶了用戶的信息,最終被賦予線程(Windows)或Cookie(Web)來對用戶進行驗證。我們在App_Code下添加CustomPrincipal和CustomIdentity來實現(xiàn)這兩個接口:

public class CustomPrincipal : IPrincipal {

    private CustomIdentity identity;

    public CustomPrincipal(CustomIdentity identity) {
        this.identity = identity;
    }

    public IIdentity Identity {
        get {
            return identity;
        }
    }

    public bool IsInRole(string role) {
        return false;
    }
}

public class CustomIdentity : IIdentity {
    private FormsAuthenticationTicket ticket;
    private HttpContext context = HttpContext.Current;

    public CustomIdentity(FormsAuthenticationTicket ticket) {
        this.ticket = ticket;
    }

    public string AuthenticationType {
        get { return "Custom"; }
    }

    public bool IsAuthenticated {
        get { return true; }
    }

    public string Name {
        get {
            return ticket.Name;
        }
    }

    public FormsAuthenticationTicket Ticket {
        get { return ticket; }
    }

    // 這里可以是任意來自數(shù)據(jù)庫的值,由Name屬性取得
    // 需要注意此時已通過身份驗證
    public string Email {
        get {
            HttpCookie cookie = context.Request.Cookies["Email"];

            if (cookie==null || String.IsNullOrEmpty(cookie.Value)) {
                string type = "jimmy_dev[at]163.com";   // 實際應(yīng)根據(jù)name屬性從數(shù)據(jù)庫中獲得
                cookie = new HttpCookie("UserType", type);
                cookie.Expires = DateTime.Now.AddDays(1);
                context.Response.Cookies.Add(cookie);
            }

            return cookie.Value;
        }
    }

    public string HomePage {
        get {
            HttpCookie cookie = context.Request.Cookies["HomePage"];

            if (cookie==null || String.IsNullOrEmpty(cookie.Value)) {
                string name = "www.tracefact.NET";      // 實際應(yīng)根據(jù)name屬性從數(shù)據(jù)庫中獲得
                cookie = new HttpCookie("NickName", name);
                cookie.Expires = DateTime.Now.AddDays(1);
                context.Response.Cookies.Add(cookie);
            }
            return cookie.Value;
        }
    }
}

注意這里的HomePage和Email這兩個屬性,它們攜帶了我們的用戶數(shù)據(jù),這里我僅僅是對它們進行了一個簡單的賦值,實際的數(shù)值應(yīng)該是來自于數(shù)據(jù)庫。還要注意獲取到它們的值后被保存在了Cookie中,以避免頻繁的對數(shù)據(jù)庫進行訪問。

定義了實現(xiàn)這兩個接口的對象之后,我們還需要把它嵌入到應(yīng)用程序的生命周期中,具體的做法就是掛接到HttpModule或者是重寫Global.asax中的事件,這里我采用了重寫Global.asax事件的方式,因此創(chuàng)建一個Global.asax文件,然后添加如下代碼:

void Application_OnPostAuthenticateRequest(object sender, EventArgs e) {
    IPrincipal user = HttpContext.Current.User;

    if (user.Identity.IsAuthenticated
        && user.Identity.AuthenticationType == "Forms") {

        FormsIdentity formIdentity = user.Identity as FormsIdentity;
        CustomIdentity identity = new CustomIdentity(formIdentity.Ticket);

        CustomPrincipal principal = new CustomPrincipal(identity);
        HttpContext.Current.User = principal;

        Thread.CurrentPrincipal = principal;
    }
}

這段代碼很好理解,它不過是在應(yīng)用程序的PostAuthenticateRequest事件中用我們自定義的CustomPrincipal和CustomIdentity替換掉了默認的IPrincipal和IIdentity實現(xiàn)。

Default.ASPx頁面預(yù)覽

我們再次對Default.ASPx進行修改,添加兩個Literal控件,用于顯示我們自定義的數(shù)值:

自定義Identity中的值:<br />
<strong>Email:</strong>
<ASP:Literal ID="ltrEmail2" runat="server"></ASP:Literal><br />

<strong>HomePage:</strong>
<ASP:Literal ID="ltrHomePage" runat="server"></ASP:Literal><br />

然后修改頁面的代碼,使用我們的自定義CustomIdentity,然后從中獲得自定義的屬性值:

protected void Page_Load(object sender, EventArgs e) {

    if (!IsPostBack) {
        if (Request.IsAuthenticated) {

            CustomIdentity identity = User.Identity as CustomIdentity;
            if (identity != null) {
                // 獲得UserData中的值
                string userData = identity.Ticket.UserData;
                Literal lbUserData = LoginView1.FindControl("lbUserData") as Literal;
                lbUserData.Text = userData;

                // 獲得identity中的值
                ltrEmail2.Text = identity.Email;
                ltrHomePage.Text = identity.HomePage;
            }
        }
    }
}

如果你現(xiàn)在打開頁面,將會看到類似下面的頁面:

可以看到我們獲得了定義在CustomIdentity中的屬性。注意這里我只是做了一個示范,因此只在CustomIdentity中包含了Email和HomePage兩個屬性值,如果看到此處你便以為大功告成,然后將所有未完成的屬性都添加到CustomIdentity中去就大錯特錯了。Identity的目的只是為你提供一個已經(jīng)登錄了的用戶的名稱,而不是攜帶所有的用戶信息,這些信息應(yīng)該由其他的類型提供。因此微軟才定義了MemberShipUser類型和Profile。從這個角度上來看,自定義IPrincipal和IIdentity并沒有太大的意義。

這里,我們最好是定義一個自己的類型來承載用戶數(shù)據(jù),下面我們就看下如何完成。

自定義類型攜帶用戶數(shù)據(jù)

在App_Code中新建一個SiteUser類,它的實現(xiàn)如下,簡單起見,我使用了公有字段而非屬性:

public class SiteUser
{
    public string Name;
    public string UserImage;
    public DateTime RegisterDate;
    public string Email;
    public string HomePage;
    public int PostCount;
    public int ReplyCount;
    public byte Level;

    public SiteUser(AuthDataSet.UserRow userRow) {
        this.Email = userRow.Email;
        this.HomePage = userRow.Homepage;
        this.Level = userRow.Level;
        this.Name = userRow.Name;
        this.PostCount = userRow.PostCount;
        this.RegisterDate = userRow.RegisterDate;
        this.ReplyCount = userRow.ReplyCount;
        this.UserImage = userRow.UserImage;
    }

    // 實際應(yīng)該由數(shù)據(jù)庫獲得
    public static SiteUser GetUser(string name) {

        AuthDataSetTableAdapters.UserTableAdapter adapter
            = new AuthDataSetTableAdapters.UserTableAdapter();
        AuthDataSet.UserDataTable userTable = adapter.GetUserTable(name);
       
        if(userTable.Rows.Count >0){
            return new SiteUser((AuthDataSet.UserRow)userTable.Rows[0]);
        }

        // 因為調(diào)用這個方法時,name應(yīng)該是有效的,
        // 如果name無效,直接拋出異常
        throw new ApplicationException("User Not Found");
    }
}

它的GetUser()靜態(tài)方法根據(jù)用戶的名稱獲得了一個SiteUser對象,這里需要注意的是通常調(diào)用這個方法時,用戶已經(jīng)登錄過了,也就是說其name參數(shù)總是有效的,因此當搜索數(shù)據(jù)庫找不到記錄時,我簡單地拋出了異常。

Default.ASPx 頁面預(yù)覽

我們再次修改Default.ASPx,添加用于顯示用戶詳細信息的控件和HTML標記:

<ASP:Panel ID="pnlUserInfo" runat="server" Visible="false">
    <table class="mainTable" style="width:280px">
        <tr>
            <th style="background:#f5f5f5;text-align:center" colspan="2">用戶信息</th>
        </tr>
        <tr>
            <td colspan="2" style="text-align: center">
                <ASP:Image ID="imgHeadImage" runat="server" />
            </td>
        </tr>
        <tr>
            <td style="width:28%;">姓名:</td>
            <td>
                <ASP:Literal ID="ltrName" runat="server"></ASP:Literal>
            </td>
        </tr>
        <tr>
            <td>注冊日期:</td>
            <td>
                <ASP:Literal ID="ltrRegisterDate" runat="server"></ASP:Literal>
            </td>
        </tr>
        <tr>
            <td>電子郵件:</td>
            <td>
                <ASP:Literal ID="ltrEmail" runat="server"></ASP:Literal>
            </td>
        </tr>
        <tr>
            <td>個人主頁:</td>
            <td>
                <ASP:HyperLink ID="lnkHomepage" runat="server"></ASP:HyperLink>
            </td>
        </tr>
        <tr>
            <td>發(fā)帖數(shù):</td>
            <td>
                <ASP:Literal ID="ltrPostCount" runat="server"></ASP:Literal>
            </td>
        </tr>
        <tr>
            <td>回帖數(shù):</td>
            <td>
                <ASP:Literal ID="ltrReplyCount" runat="server"></ASP:Literal>
            </td>
        </tr>
        <tr>
            <td>用戶等別:</td>
            <td>
                <ASP:Image ID="imgUserLevel" runat="server" />
            </td>
        </tr>
    </table>
</ASP:Panel>

然后修改頁面的后置代碼:

protected void Page_Load(object sender, EventArgs e) {
    if (!IsPostBack) {
        if (Request.IsAuthenticated) {

            // 上面相同

            SiteUser user = SiteUser.GetUser(identity.Name);
            PopulateControls(user);
        }
    }
}

private void PopulateControls(SiteUser user) {
    ltrEmail.Text = user.Email;
    ltrName.Text = user.Name;
    ltrPostCount.Text = user.PostCount.ToString();
    ltrRegisterDate.Text = user.RegisterDate.ToShortDateString();
    ltrReplyCount.Text = user.ReplyCount.ToString();
    lnkHomepage.Text = user.HomePage;
    lnkHomepage.NavigateUrl = user.HomePage;

    imgHeadImage.ImageUrl = "~/Images/" + user.UserImage;
    imgUserLevel.ImageUrl =
        String.Format("~/Images/Star{0}.gif", user.Level);

    pnlUserInfo.Visible = true;
}

這里,我們從Identity中獲得用戶的名稱,然后再調(diào)用SiteUser的GetUser()方法,獲得了一個SiteUser對象,最后使用這個SiteUser對象對頁面控件進行了賦值。下面是最后的頁面效果:

總結(jié)

在這篇文章中我們看到了如何使用ASP.NET內(nèi)置機制實現(xiàn)用戶驗證的功能,并且通過FormsAuthenticationTicket的UserData屬性、自定義IPrincipal和IIdentity、以及自定義對象三種方式實現(xiàn)了附加 (獲取)用戶數(shù)據(jù)。

本文所附帶的代碼中,還有一些頁面只有登錄用戶才能進行訪問,但在這篇文章中我沒有演示和說明。使用.NET的驗證機制,我們可以通過僅在Web.config設(shè)置一下,就擁有了以前需要編碼才能實現(xiàn)的限制頁面訪問的功能。通過這篇文章,我希望大家能夠看到,大多數(shù)情況下,我們都可以使用.NET的自定義驗證系統(tǒng),而沒有必要再重復(fù)造輪子去實現(xiàn)自己的驗證方式。

感謝閱讀,希望這篇文章能給你帶來幫助。

NET技術(shù)Asp.Net 用戶驗證(自定義IPrincipal和IIdentity),轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 国产精品亚洲二区在线 | 久热精品视频在线观看99小说 | 一区二区三区视频在线播放 | 久久精品亚洲 | 日韩α片 | 国产久爱青草视频在线观看 | 婷婷99视频精品全部在线观看 | 激情五月社区 | 91精品久久久久久久久网影视 | 精品视频一区二区三三区四区 | 香蕉人人超人人超免费看视频 | 92看片淫黄大片看国产片 | 欧美日韩一区二区在线观看视频 | 最近更新中文字幕第一页 | 91在线免费播放 | 美女一级毛片免费观看 | 麻豆播放| 成人在线免费视频观看 | 韩国精品一区二区三区四区五区 | 亚洲国产天堂久久综合 | 视频在线亚洲 | 欧美日韩亚洲区久久综合 | 91中文字幕在线一区 | 国产福利在线免费观看 | 久久一区二区三区免费播放 | 99国产国人青青视频在线观看 | 美女黄影院 | 中文字幕久久网 | 久久伊人久久亚洲综合 | 久久久久久久久久毛片精品美女 | 五月婷婷亚洲综合 | 丁香六月纪婷婷激情综合 | 精品国产欧美一区二区三区成人 | 亚洲影视精品 | 丁香在线| 亚洲香蕉久久一区二区三区四区 | 免费看黄色录像 | 思99re久久这里只有精品首页 | 亚洲国产精品综合久久一线 | 16女性下面扒开无遮挡免费 | 日本免费久久久久久久网站 |