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

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

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

引言

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

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

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

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

開始前的準(zhǔn)備

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

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

接著對(duì)Web.config進(jìn)行一下配置,首先看根目錄下的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驗(yàn)證,并且設(shè)置用戶身份驗(yàn)證過期時(shí)間為600分鐘(默認(rèn)為30分鐘),slidingExpiration的意思是說timeout采用絕對(duì)時(shí)間還是滑動(dòng)時(shí)間,當(dāng)采用滑動(dòng)時(shí)間時(shí),如果在timeout時(shí)間內(nèi)再次瀏覽頁(yè)面,用戶的最后活躍時(shí)間將設(shè)為當(dāng)前時(shí)間,并重新開始計(jì)算,這里我們采用滑動(dòng)時(shí)間。loginUrl指定了登錄頁(yè)面,當(dāng)匿名用戶訪問需要驗(yàn)證后才能訪問的頁(yè)面時(shí),將會(huì)到自動(dòng)導(dǎo)航到這里所設(shè)置的SignIn.ASPx頁(yè)面,默認(rèn)為L(zhǎng)ogin.ASPx。

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

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

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

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

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

接下來我們需要添加一個(gè)存儲(chǔ)過程,這個(gè)存儲(chǔ)過程接收一個(gè)name參數(shù),和一個(gè)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,則無(wú)法進(jìn)行區(qū)分。由數(shù)據(jù)庫(kù)帶回了正確的密碼之后,我們只需要在程序中與用戶輸入的密碼進(jìn)行對(duì)比就可以知道用戶的密碼是否正確。

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

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

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

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

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

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(強(qiáng)類型)。好了,現(xiàn)在我們的數(shù)據(jù)訪問就已經(jīng)OK了,接下來我們看一下第一個(gè)頁(yè)面:SignIn.ASPx用戶登錄頁(yè)面。

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

Login.ASPx頁(yè)面實(shí)現(xiàn)

在登錄頁(yè)面,我們需要針對(duì)登錄用戶和非登錄用戶做不同的處理:如果用戶尚未登錄,則顯示登錄用的表單;如果用于已經(jīng)登錄了,則顯示登錄用戶名并進(jìn)行提示。完成這件事最好就是使用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屬性,它是一個(gè)FormsAuthenticationTicket類型,它含有一個(gè)UserData字符串屬性可以用于承載我們的用戶數(shù)據(jù),遺憾的是這個(gè)屬性是只讀的,為了給這個(gè)屬性賦值,我們需要重新新構(gòu)建一個(gè)FormsAuthenticationTicket,并在構(gòu)造函數(shù)中傳入我們想要添加的用戶信息。FormasAuthenticationTicket包含了諸多用于用戶驗(yàn)證的信息,它從Cookie中獲得,可以認(rèn)為它是服務(wù)端對(duì)Cookie的一個(gè)包裝,只是這里的Cookie的操作不需要我們來處理,而由ASP.NET運(yùn)行時(shí)去處理。具體的代碼如下:

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 = "登錄時(shí)間" + DateTime.Now.ToString();

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


    // 添加自定義的值,然后導(dǎo)航到來到此頁(yè)面之前的位置
    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);

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

        Response.Redirect(url);
    }
}

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

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

Default.ASPx 頁(yè)面預(yù)覽

默認(rèn)情況下SignIn.ASPx在登錄成功后會(huì)導(dǎo)航到Default.ASPx頁(yè)面,所以我們先簡(jiǎn)單的構(gòu)建一下Default.ASPx頁(yè)面,看看實(shí)現(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"  />

類似地,我們放置了一個(gè)LoginView控件,只是這里我們多放置了一個(gè)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;
        }
    }
}

最后我們先進(jìn)行登錄,然后再打開Default.ASPx頁(yè)面,會(huì)看到類似這樣的輸出:

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

自定義IPrincipal和IIdentity

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

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ù)庫(kù)的值,由Name屬性取得
    // 需要注意此時(shí)已通過身份驗(yàn)證
    public string Email {
        get {
            HttpCookie cookie = context.Request.Cookies["Email"];

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

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

定義了實(shí)現(xiàn)這兩個(gè)接口的對(duì)象之后,我們還需要把它嵌入到應(yīng)用程序的生命周期中,具體的做法就是掛接到HttpModule或者是重寫Global.asax中的事件,這里我采用了重寫Global.asax事件的方式,因此創(chuàng)建一個(gè)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替換掉了默認(rèn)的IPrincipal和IIdentity實(shí)現(xiàn)。

Default.ASPx頁(yè)面預(yù)覽

我們?cè)俅螌?duì)Default.ASPx進(jìn)行修改,添加兩個(gè)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 />

然后修改頁(yè)面的代碼,使用我們的自定義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)在打開頁(yè)面,將會(huì)看到類似下面的頁(yè)面:

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

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

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

在App_Code中新建一個(gè)SiteUser類,它的實(shí)現(xiàn)如下,簡(jiǎ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;
    }

    // 實(shí)際應(yīng)該由數(shù)據(jù)庫(kù)獲得
    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]);
        }

        // 因?yàn)檎{(diào)用這個(gè)方法時(shí),name應(yīng)該是有效的,
        // 如果name無(wú)效,直接拋出異常
        throw new ApplicationException("User Not Found");
    }
}

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

Default.ASPx 頁(yè)面預(yù)覽

我們?cè)俅涡薷腄efault.ASPx,添加用于顯示用戶詳細(xì)信息的控件和HTML標(biāo)記:

<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>注冊(cè)日期:</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>個(gè)人主頁(yè):</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>

然后修改頁(yè)面的后置代碼:

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()方法,獲得了一個(gè)SiteUser對(duì)象,最后使用這個(gè)SiteUser對(duì)象對(duì)頁(yè)面控件進(jìn)行了賦值。下面是最后的頁(yè)面效果:

總結(jié)

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

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

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

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

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

主站蜘蛛池模板: 亚洲国产高清视频 | 91国内揄拍·国内精品对白 | 欧美色国 | 牛牛影院成人免费网页 | 亚洲国产婷婷香蕉久久久久久 | 四虎现在的网址入口2022 | 中文一区在线 | 亚洲国产精品久久久天堂麻豆 | 亚洲第一二三四区 | 久久久影院亚洲精品 | 亚洲乱码在线观看 | 日本不卡一区二区三区在线观看 | 国产欧美一区二区三区观看 | 国产激情视频在线观看 | 久久999精品 | 377日本大胆欧美人术艺术 | 中文字幕在线视频第一页 | 日韩免费毛片 | 2022国产91精品久久久久久 | 色爽视频| 国产精品欧美亚洲韩国日本久久 | 天天干天天干天天天天天天爽 | 色哟哟网站在线观看 | 欧美日韩一区二区亚洲 | 天天色天天操综合网 | 国产图片综合区 | 久久久精品国产四虎影视 | 国产在线精品国自产拍影院同性 | 美女视频黄视频 | 免费国产成人手机在线观看 | 国产午夜大片 | 亚洲视频在线免费播放 | 欧美大尺度无遮挡性视频 | 白嫩少妇激情无码 | 亚洲一区免费在线 | 91福利国产在线观一区二区 | 久久亚洲视频 | 免费超爽大片黄网站 | 国产人成精品综合欧美成人 | 激情在线播放免费视频高清 | 欧美激情乱人伦 |