本文轉自:http://www.cnblogs.com/amylis_chen/archive/2012/08/02/2620129.html
Default.aspx 頁面預覽 預設情況下SignIn.aspx在登入成功後會導航到Default.aspx頁面,所以我們先簡單的構建一下Default.aspx頁面,看看實現的效果: <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頁面,會看到類似這樣的輸出: 至此,我們已經看到了如何利用FormsAuthentionTicket來附帶額外的使用者資料,但是我們應該看到這種做法存在的問題:可以儲存的資料過於單一,僅僅只是一個字串。而我們第一節中所介紹的使用者表包括各種型別的各種資料。如果你看過了 從一個範例看XML的應用 這篇文章,你應該立刻想到此處又是一個“單一字串儲存多種不同型別資料”的應用場景,我們可以定義XML來解決。對於這種方式,我不再演示了。實際上,我們可以自定義一個IPrincipal和IIdentity來完成,接下來就來看一下。 自定義IPrincipal和IIdentity 不管是在Windows上還是在Web上,.Net都使用這兩個介面來實現使用者的身份驗證。它們不過是一個介面,實現了這兩個介面的型別附帶了使用者的資訊,最終被賦予執行緒(Windows)或Cookie(Web)來對使用者進行驗證。我們在App_Code下新增CustomPrincipal和CustomIdentity來實現這兩個介面: 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; }
} // 這裡可以是任意來自資料庫的值,由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"; // 實際應根據name屬性從資料庫中獲得
cookie = new HttpCookie("UserType", type);
cookie.Expires = DateTime.Now.AddDays();
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"; // 實際應根據name屬性從資料庫中獲得
cookie = new HttpCookie("NickName", name);
cookie.Expires = DateTime.Now.AddDays();
context.Response.Cookies.Add(cookie);
}
return cookie.Value;
}
}
} 注意這裡的HomePage和Email這兩個屬性,它們攜帶了我們的使用者資料,這裡我僅僅是對它們進行了一個簡單的賦值,實際的數值應該是來自於資料庫。還要注意獲取到它們的值後被儲存在了Cookie中,以避免頻繁的對資料庫進行訪問。 定義了實現這兩個介面的物件之後,我們還需要把它嵌入到應用程式的生命週期中,具體的做法就是掛接到HttpModule或者是重寫Global.asax中的事件,這裡我採用了重寫Global.asax事件的方式,因此建立一個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;
}
} 這段程式碼很好理解,它不過是在應用程式的PostAuthenticateRequest事件中用我們自定義的CustomPrincipal和CustomIdentity替換掉了預設的IPrincipal和IIdentity實現。 Default.aspx頁面預覽 我們再次對Default.aspx進行修改,新增兩個Literal控制元件,用於顯示我們自定義的數值: 自定義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;
}
}
}
} 如果你現在開啟頁面,將會看到類似下面的頁面: 可以看到我們獲得了定義在CustomIdentity中的屬性。注意這裡我只是做了一個示範,因此只在CustomIdentity中包含了Email和HomePage兩個屬性值,如果看到此處你便以為大功告成,然後將所有未完成的屬性都新增到CustomIdentity中去就大錯特錯了。Identity的目的只是為你提供一個已經登入了的使用者的名稱,而不是攜帶所有的使用者資訊,這些資訊應該由其他的型別提供。因此微軟才定義了MemberShipUser型別和Profile。從這個角度上來看,自定義IPrincipal和IIdentity並沒有太大的意義。 這裡,我們最好是定義一個自己的型別來承載使用者資料,下面我們就看下如何完成。 自定義型別攜帶使用者資料 在App_Code中新建一個SiteUser類,它的實現如下,簡單起見,我使用了公有欄位而非屬性: 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;
} // 實際應該由資料庫獲得
public static SiteUser GetUser(string name) { AuthDataSetTableAdapters.UserTableAdapter adapter
= new AuthDataSetTableAdapters.UserTableAdapter();
AuthDataSet.UserDataTable userTable = adapter.GetUserTable(name); if(userTable.Rows.Count >){
return new SiteUser((AuthDataSet.UserRow)userTable.Rows[]);
} // 因為呼叫這個方法時,name應該是有效的,
// 如果name無效,直接丟擲異常
throw new ApplicationException("User Not Found");
}
} 它的GetUser()靜態方法根據使用者的名稱獲得了一個SiteUser物件,這裡需要注意的是通常呼叫這個方法時,使用者已經登入過了,也就是說其name引數總是有效的,因此當搜尋資料庫找不到記錄時,我簡單地丟擲了異常。