婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av

主頁 > 知識庫 > ASP.NET Forms身份認證詳解

ASP.NET Forms身份認證詳解

熱門標簽:海東防封電銷卡 南昌自動外呼系統線路 西寧電銷外呼系統公司 安陸市地圖標注app 云南外呼系統代理 辦公用地圖標注網點怎么操作 聊城智能電銷機器人電話 上海市三維地圖標注 寧德防封版電銷卡

ASP.NET身份認證基礎

在開始今天的內容之前,我想有二個最基礎的問題首先要明確:

1. 如何判斷當前請求是一個已登錄用戶發起的?

2. 如何獲取當前登錄用戶的登錄名?

在標準的ASP.NET身份認證方式中,上面二個問題的答案是:

1. 如果Request.IsAuthenticated為true,則表示是一個已登錄用戶。

2. 如果是一個已登錄用戶,訪問HttpContext.User.Identity.Name可獲取登錄名(都是實例屬性)。

接下來,本文將會圍繞上面二個問題展開,請繼續閱讀。

ASP.NET身份認證過程

在ASP.NET中,整個身份認證的過程其實可分為二個階段:認證與授權。

1. 認證階段:識別當前請求的用戶是不是一個可識別(的已登錄)用戶。

2. 授權階段:是否允許當前請求訪問指定的資源。

這二個階段在ASP.NET管線中用AuthenticateRequest和AuthorizeRequest事件來表示。

在認證階段,ASP.NET會檢查當前請求,根據web.config設置的認證方式,嘗試構造HttpContext.User對象供我們在后續的處理中使用。在授權階段,會檢查當前請求所訪問的資源是否允許訪問,因為有些受保護的頁面資源可能要求特定的用戶或者用戶組才能訪問。所以,即使是一個已登錄用戶,也有可能會不能訪問某些頁面。當發現用戶不能訪問某個頁面資源時,ASP.NET會將請求重定向到登錄頁面。

受保護的頁面與登錄頁面我們都可以在web.config中指定,具體方法可參考后文。

在ASP.NET中,Forms認證是由FormsAuthenticationModule實現的,URL的授權檢查是由UrlAuthorizationModule實現的。

如何實現登錄與注銷

前面我介紹了可以使用Request.IsAuthenticated來判斷當前用戶是不是一個已登錄用戶,那么這一過程又是如何實現的呢?

為了回答這個問題,我準備了一個簡單的示例頁面,代碼如下:

fieldset>legend>用戶狀態/legend>form action="%= Request.RawUrl %>" method="post"> 
 % if( Request.IsAuthenticated ) { %> 
  當前用戶已登錄,登錄名:%= Context.User.Identity.Name.HtmlEncode() %> br />    
  input type="submit" name="Logon" value="退出" /> 
 % } else { %> 
  b>當前用戶還未登錄。/b> 
 % } %>    
/form>/fieldset> 

頁面顯示效果如下:

根據前面的代碼,我想現在能看到這個頁面顯示也是正確的,是的,我目前還沒有登錄(根本還沒有實現這個功能)。

下面我再加點代碼來實現用戶登錄。頁面代碼:

fieldset>legend>普通登錄/legend>form action="%= Request.RawUrl %>" method="post"> 
 登錄名:input type="text" name="loginName" style="width: 200px" value="Fish" /> 
 input type="submit" name="NormalLogin" value="登錄" /> 
/form>/fieldset> 

現在頁面的顯示效果:

登錄與退出登錄的實現代碼:

public void Logon() 
{ 
 FormsAuthentication.SignOut(); 
} 
 
public void NormalLogin() 
{ 
 // ----------------------------------------------------------------- 
 // 注意:演示代碼為了簡單,這里不檢查用戶名與密碼是否正確。 
 // ----------------------------------------------------------------- 
 
 string loginName = Request.Form["loginName"]; 
 if( string.IsNullOrEmpty(loginName) ) 
  return; 
  
 FormsAuthentication.SetAuthCookie(loginName, true); 
 
 TryRedirect(); 
} 

現在,我可試一下登錄功能。點擊登錄按鈕后,頁面的顯示效果如下:

從圖片的顯示可以看出,我前面寫的NormalLogin()方法確實可以實現用戶登錄。
當然了,我也可以在此時點擊退出按鈕,那么就回到了圖片2的顯示。

寫到這里,我想有必要再來總結一下在ASP.NET中實現登錄與注銷的方法:

1. 登錄:調用FormsAuthentication.SetAuthCookie()方法,傳遞一個登錄名即可。

2. 注銷:調用FormsAuthentication.SignOut()方法。

保護受限制的頁面

在一個ASP.NET網站中,有些頁面會允許所有用戶訪問,包括一些未登錄用戶,但有些頁面則必須是已登錄用戶才能訪問,還有一些頁面可能會要求特定的用戶或者用戶組的成員才能訪問。這類頁面因此也可稱為【受限頁面】,它們一般代表著比較重要的頁面,包含一些重要的操作或功能。

為了保護受限制的頁面的訪問,ASP.NET提供了一種簡單的方式:可以在web.config中指定受限資源允許哪些用戶或者用戶組(角色)的訪問,也可以設置為禁止訪問。

比如,網站有一個頁面:MyInfo.aspx,它要求訪問這個頁面的訪問者必須是一個已登錄用戶,那么可以在web.config中這樣配置:

location path="MyInfo.aspx"> 
 system.web> 
  authorization> 
   deny users="?"/> 
  /authorization> 
 /system.web> 
/location> 

為了方便,我可能會將一些管理相關的多個頁面放在Admin目錄中,顯然這些頁面只允許Admin用戶組的成員才可以訪問。對于這種情況,我們可以直接針對一個目錄設置訪問規則:

location path="Admin"> 
 system.web> 
  authorization> 
   allow roles="Admin"/> 
   deny users="*"/> 
  /authorization> 
 /system.web> 
/location> 

這樣就不必一個一個頁面單獨設置了,還可以在目錄中創建一個web.config來指定目錄的訪問規則,請參考后面的示例。

在前面的示例中,有一點要特別注意的是:

1. allow和deny之間的順序一定不能寫錯了,UrlAuthorizationModule將按這個順序依次判斷。

2. 如果某個資源只允許某類用戶訪問,那么最后的一條規則一定是 deny users="*" />

在allow和deny的配置中,我們可以在一條規則中指定多個用戶:

1. 使用users屬性,值為逗號分隔的用戶名列表。

2. 使用roles屬性,值為逗號分隔的角色列表。

3. 問號 (?) 表示匿名用戶。

4. 星號 (*) 表示所有用戶。

登錄頁不能正常顯示的問題

有時候,我們可能要開發一個內部使用的網站程序,這類網站程序要求 禁止匿名用戶的訪問,即:所有使用者必須先登錄才能訪問。因此,我們通常會在網站根目錄下的web.config中這樣設置:

authorization> 
 deny users="?"/> 
/authorization> 

對于我們的示例,我們也可以這樣設置。此時在瀏覽器打開頁面時,呈現效果如下:

從圖片中可以看出:頁面的樣式顯示不正確,最下邊還多出了一行文字。

這個頁面的完整代碼是這樣的(它引用了一個CSS文件和一個JS文件):

%@ Page Language="C#" CodeFile="Default.aspx.cs" Inherits="_Default" %> 
html xmlns="http://www.w3.org/1999/xhtml"> 
head> 
 title>FormsAuthentication DEMO - http://www.cnblogs.com/fish-li//title> 
 link type="text/css" rel="Stylesheet" href="css/StyleSheet.css" /> 
/head> 
body> 
 fieldset>legend>普通登錄/legend>form action="%= Request.RawUrl %>" method="post"> 
  登錄名:input type="text" name="loginName" style="width: 200px" value="Fish" /> 
  input type="submit" name="NormalLogin" value="登錄" /> 
 /form>/fieldset>  
  
 fieldset>legend>用戶狀態/legend>form action="%= Request.RawUrl %>" method="post"> 
  % if( Request.IsAuthenticated ) { %> 
   當前用戶已登錄,登錄名:%= Context.User.Identity.Name.HtmlEncode() %> br /> 
    
   % var user = Context.User as MyFormsPrincipalUserInfo>; %> 
   % if( user != null ) { %> 
    %= user.UserData.ToString().HtmlEncode() %> 
   % } %> 
    
   input type="submit" name="Logon" value="退出" /> 
  % } else { %> 
   b>當前用戶還未登錄。/b> 
  % } %>    
 /form>/fieldset>  
  
 p id="hideText">i>不應該顯示的文字/i>/p> 
 script type="text/javascript" src="js/JScript.js">/script> 
/body> 
/html> 

頁面最后一行文字平時不顯示是因為JScript.js中有以下代碼:

document.getElementById("hideText").setAttribute("style", "display: none"); 

這段JS代碼能做什么,我想就不用再解釋了。雖然這段JS代碼沒什么價值,但我主要是想演示在登錄頁面中引用JS的場景。

根據前面圖片,我們可以猜測到:應該是CSS和JS文件沒有正確加載造成的。為了確認就是這樣原因,我們可以打開FireBug再來看一下頁面加載情況:

根據FireBug提供的線索我們可以分析出,頁面在訪問CSS, JS文件時,其實是被重定向到登錄頁面了,因此獲得的結果肯定也是無意義的,所以就造成了登錄頁的顯示不正確。

還記得【授權】嗎?
是的,現在就是由于我們在web.config中設置了不允許匿名用戶訪問,因此,所有的資源也就不允許匿名用戶訪問了,包括登錄頁所引用的CSS, JS文件。當授權檢查失敗時,請求會被重定向到登錄頁面,所以,登錄頁本身所引用的CSS, JS文件最后得到的響應內容其實是登錄頁的HTML代碼,最終導致它們不能發揮作用,表現為登錄頁的樣式顯示不正確,以及引用的JS文件也不起作用。

不過,有一點比較奇怪:為什么訪問登錄頁面時,沒有發生重定向呢?

原因是這樣的:在ASP.NET內部,當發現是在訪問登錄面時,會設置HttpContext.SkipAuthorization = true (其實是一個內部調用),這樣的設置會告訴后面的授權檢查模塊:跳過這次請求的授權檢查。 因此,登錄頁總是允許所有用戶訪問,但是CSS文件以及JS文件是在另外的請求中發生的,那些請求并不會要跳過授權模塊的檢查。

為了解決登錄頁不能正確顯示的問題,我們可以這樣處理:

1. 在網站根目錄中的web.config中設置登錄頁所引用的JS, CSS文件都允許匿名訪問。

2. 也可以直接針對JS, CSS目錄設置為允許匿名用戶訪問。

3. 還可以在CSS, JS目錄中創建一個web.config文件來配置對應目錄的授權規則。可參考以下web.config文件:

?xml version="1.0"?> 
configuration> 
 system.web> 
  authorization> 
   allow users="*"/> 
  /authorization> 
 /system.web> 
/configuration> 

第三種做法可以不修改網站根目錄下的web.config文件。

注意:在IIS中看到的情況就和在Visual Studio中看到的結果就不一樣了。 因為,像js, css, image這類文件屬于靜態資源文件,IIS能直接處理,不需要交給ASP.NET來響應,因此就不會發生授權檢查失敗,所以,如果這類網站部署在IIS中,看到的結果又是正常的。

認識Forms身份認證

前面我演示了如何用代碼實現登錄與注銷的過程,下面再來看一下登錄時,ASP.NET到底做了些什么事情,它是如何知道當前請求是一個已登錄用戶的?

在繼續探索這個問題前,我想有必要來了解一下HTTP協議的一些特點。

HTTP是一個無狀態的協議,無狀態的意思可以理解為: WEB服務器在處理所有傳入請求時,根本就不知道某個請求是否是一個用戶的第一次請求與后續請求,或者是另一個用戶的請求。 WEB服務器每次在處理請求時,都會按照用戶所訪問的資源所對應的處理代碼,從頭到尾執行一遍,然后輸出響應內容, WEB服務器根本不會記住已處理了哪些用戶的請求,因此,我們通常說HTTP協議是無狀態的。

雖然HTTP協議與WEB服務器是無狀態,但我們的業務需求卻要求有狀態,典型的就是用戶登錄,在這種業務需求中,要求WEB服務器端能區分某個請求是不是一個已登錄用戶發起的,或者當前請求是哪個用戶發出的。在開發WEB應用程序時,我們通常會使用Cookie來保存一些簡單的數據供服務端維持必要的狀態。既然這是個通常的做法,那我們現在就來看一下現在頁面的Cookie使用情況吧,以下是我用FireFox所看到的Cookie列表:

這個名字:LoginCookieName,是我在web.config中指定的:

authentication mode="Forms" > 
 forms cookieless="UseCookies" name="LoginCookieName" loginUrl="~/Default.aspx">/forms> 
/authentication> 

在這段配置中,我不僅指定的登錄狀態的Cookie名,還指定了身份驗證模式,以及Cookie的使用方式。

為了判斷這個Cookie是否與登錄狀態有關,我們可以在瀏覽器提供的界面刪除它,然后刷新頁面,此時頁面的顯示效果如下:

此時,頁面顯示當前用戶沒有登錄。

為了確認這個Cookie與登錄狀態有關,我們可以重新登錄,然后再退出登錄。
發現只要是頁面顯示當前用戶未登錄時,這個Cookie就不會存在。

事實上,通過SetAuthCookie這個方法名,我們也可以猜得出這個操作會寫一個Cookie。
注意:本文不討論無Cookie模式的Forms登錄。

從前面的截圖我們可以看出:雖然當前用戶名是 Fish ,但是,Cookie的值是一串亂碼樣的字符串。
由于安全性的考慮,ASP.NET對Cookie做過加密處理了,這樣可以防止惡意用戶構造Cookie繞過登錄機制來模擬登錄用戶。如果想知道這串加密字符串是如何得到的,那么請參考后文。

小結:

1. Forms身份認證是在web.config中指定的,我們還可以設置Forms身份認證的其它配置參數。

2. Forms身份認證的登錄狀態是通過Cookie來維持的。

3. Forms身份認證的登錄Cookie是加密的。

理解Forms身份認證

經過前面的Cookie分析,我們可以發現Cookie的值是一串加密后的字符串,現在我們就來分析這個加密過程以及Cookie對于身份認證的作用。

登錄的操作通常會檢查用戶提供的用戶名和密碼,因此登錄狀態也必須具有足夠高的安全性。在Forms身份認證中,由于登錄狀態是保存在Cookie中,而Cookie又會保存到客戶端,因此,為了保證登錄狀態不被惡意用戶偽造, ASP.NET采用了加密的方式保存登錄狀態。為了實現安全性,ASP.NET采用【Forms身份驗證憑據】(即FormsAuthenticationTicket對象)來表示一個Forms登錄用戶,加密與解密由FormsAuthentication的Encrypt與Decrypt的方法來實現。

用戶登錄的過程大致是這樣的:

1. 檢查用戶提交的登錄名和密碼是否正確。

2. 根據登錄名創建一個FormsAuthenticationTicket對象。

3. 調用FormsAuthentication.Encrypt()加密。

4. 根據加密結果創建登錄Cookie,并寫入Response。

在登錄驗證結束后,一般會產生重定向操作,那么后面的每次請求將帶上前面產生的加密Cookie,供服務器來驗證每次請求的登錄狀態。

每次請求時的(認證)處理過程如下:

1. FormsAuthenticationModule嘗試讀取登錄Cookie。

2. 從Cookie中解析出FormsAuthenticationTicket對象。過期的對象將被忽略。

3. 根據FormsAuthenticationTicket對象構造FormsIdentity對象并設置HttpContext.Usre

4. UrlAuthorizationModule執行授權檢查。

在登錄與認證的實現中,FormsAuthenticationTicket和FormsAuthentication是二個核心的類型,前者可以認為是一個數據結構,后者可認為是處理前者的工具類。

UrlAuthorizationModule是一個授權檢查模塊,其實它與登錄認證的關系較為獨立,因此,如果我們不使用這種基于用戶名與用戶組的授權檢查,也可以禁用這個模塊。

由于Cookie本身有過期的特點,然而為了安全,FormsAuthenticationTicket也支持過期策略,不過,ASP.NET的默認設置支持FormsAuthenticationTicket的可調過期行為,即:slidingExpiration=true 。這二者任何一個過期時,都將導致登錄狀態無效。

FormsAuthenticationTicket的可調過期的主要判斷邏輯由FormsAuthentication.RenewTicketIfOld方法實現,代碼如下:

public static FormsAuthenticationTicket RenewTicketIfOld(FormsAuthenticationTicket tOld) 
{ 
 // 這段代碼是意思是:當指定的超時時間逝去大半時將更新FormsAuthenticationTicket對象。 
 
 if( tOld == null ) 
  return null; 
  
 DateTime now = DateTime.Now; 
 TimeSpan span = (TimeSpan)(now - tOld.IssueDate); 
 TimeSpan span2 = (TimeSpan)(tOld.Expiration - now); 
 if( span2 > span ) 
  return tOld; 
  
 return new FormsAuthenticationTicket(tOld.Version, tOld.Name, 
  now, now + (tOld.Expiration - tOld.IssueDate), 
  tOld.IsPersistent, tOld.UserData, tOld.CookiePath); 
} 
Request.IsAuthenticated可以告訴我們當前請求是否已經過身份驗證,我們來看一下這個屬性是如何實現的:

public bool IsAuthenticated 
{ 
 get 
 { 
  return (((this._context.User != null) 
    (this._context.User.Identity != null)) 
    this._context.User.Identity.IsAuthenticated); 
 } 
} 

從代碼可以看出,它的返回結果基本上來源于對Context.User的判斷。
另外,由于User和Identity都是二個接口類型的屬性,因此,不同的實現方式對返回值也有影響。

由于可能會經常使用HttpContext.User這個實例屬性,為了讓它能正常使用, DefaultAuthenticationModule會在ASP.NET管線的PostAuthenticateRequest事件中檢查此屬性是否為null,如果它為null,DefaultAuthenticationModule會給它一個默認的GenericPrincipal對象,此對象指示一個未登錄的用戶。

我認為ASP.NET的身份認證的最核心部分其實就是HttpContext.User這個屬性所指向的對象。為了更好了理解Forms身份認證,我認為自己重新實現User這個對象的接口會有較好的幫助。

實現自定義的身份認證標識

前面演示了最簡單的ASP.NET Forms身份認證的實現方法,即:直接調用SetAuthCookie方法。不過調用這個方法,只能傳遞一個登錄名。但是有時候為了方便后續的請求處理,還需要保存一些與登錄名相關的額外信息。雖然知道ASP.NET使用Cookie來保存登錄名狀態信息,我們也可以直接將前面所說的額外信息直接保存在Cookie中,但是考慮安全性,我們還需要設計一些加密方法,而且還需要考慮這些額外信息保存在哪里才能方便使用,并還要考慮隨登錄與注銷同步修改。因此,實現這些操作還是有點繁瑣的。

為了保存與登錄名相關的額外的用戶信息,我認為實現自定義的身份認證標識(HttpContext.User實例)是個容易的解決方法。
理解這個方法也會讓我們對Forms身份認證有著更清楚地認識。

這個方法的核心是(分為二個子過程):

1. 在登錄時,創建自定義的FormsAuthenticationTicket對象,它包含了用戶信息。

2. 加密FormsAuthenticationTicket對象。

3. 創建登錄Cookie,它將包含FormsAuthenticationTicket對象加密后的結果。

4. 在管線的早期階段,讀取登錄Cookie,如果有,則解密。

5. 從解密后的FormsAuthenticationTicket對象中還原我們保存的用戶信息。

6. 設置HttpContext.User為我們自定義的對象。

現在,我們還是來看一下HttpContext.User這個屬性的定義:

// 為當前 HTTP 請求獲取或設置安全信息。 
// 
// 返回結果: 
//  當前 HTTP 請求的安全信息。 
public IPrincipal User { get; set; } 

由于這個屬性只是個接口類型,因此,我們也可以自己實現這個接口。

考慮到更好的通用性:不同的項目可能要求接受不同的用戶信息類型。所以,我定義了一個泛型類。

public class MyFormsPrincipalTUserData> : IPrincipal 
 where TUserData : class, new() 
{ 
 private IIdentity _identity; 
 private TUserData _userData; 
 
 public MyFormsPrincipal(FormsAuthenticationTicket ticket, TUserData userData) 
 { 
  if( ticket == null ) 
   throw new ArgumentNullException("ticket"); 
  if( userData == null ) 
   throw new ArgumentNullException("userData"); 
 
  _identity = new FormsIdentity(ticket); 
  _userData = userData; 
 } 
  
 public TUserData UserData 
 { 
  get { return _userData; } 
 } 
 
 public IIdentity Identity 
 { 
  get { return _identity; } 
 } 
 
 public bool IsInRole(string role) 
 { 
  // 把判斷用戶組的操作留給UserData去實現。 
 
  IPrincipal principal = _userData as IPrincipal; 
  if( principal == null ) 
   throw new NotImplementedException(); 
  else 
   return principal.IsInRole(role); 
 } 

與之配套使用的用戶信息的類型定義如下(可以根據實際情況來定義):

public class UserInfo : IPrincipal 
{ 
 public int UserId; 
 public int GroupId; 
 public string UserName; 
  
 // 如果還有其它的用戶信息,可以繼續添加。 
 
 public override string ToString() 
 { 
  return string.Format("UserId: {0}, GroupId: {1}, UserName: {2}, IsAdmin: {3}", 
   UserId, GroupId, UserName, IsInRole("Admin")); 
 } 
 
 #region IPrincipal Members 
 
 [ScriptIgnore] 
 public IIdentity Identity 
 { 
  get { throw new NotImplementedException(); } 
 } 
 
 public bool IsInRole(string role) 
 { 
  if( string.Compare(role, "Admin", true) == 0 ) 
   return GroupId == 1; 
  else 
   return GroupId > 0; 
 } 
 
 #endregion 
} 

注意:表示用戶信息的類型并不要求一定要實現IPrincipal接口,如果不需要用戶組的判斷,可以不實現這個接口。

登錄時需要調用的方法(定義在MyFormsPrincipal類型中):

/// summary> 
/// 執行用戶登錄操作 
/// /summary> 
/// param name="loginName">登錄名/param> 
/// param name="userData">與登錄名相關的用戶信息/param> 
/// param name="expiration">登錄Cookie的過期時間,單位:分鐘。/param> 
public static void SignIn(string loginName, TUserData userData, int expiration) 
{ 
 if( string.IsNullOrEmpty(loginName) ) 
  throw new ArgumentNullException("loginName"); 
 if( userData == null ) 
  throw new ArgumentNullException("userData"); 
 
 // 1. 把需要保存的用戶數據轉成一個字符串。 
 string data = null; 
 if( userData != null ) 
  data = (new JavaScriptSerializer()).Serialize(userData); 
 
 
 // 2. 創建一個FormsAuthenticationTicket,它包含登錄名以及額外的用戶數據。 
 FormsAuthenticationTicket ticket = new FormsAuthenticationTicket( 
  2, loginName, DateTime.Now, DateTime.Now.AddDays(1), true, data); 
 
 
 // 3. 加密Ticket,變成一個加密的字符串。 
 string cookieValue = FormsAuthentication.Encrypt(ticket); 
 
 
 // 4. 根據加密結果創建登錄Cookie 
 HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue); 
 cookie.HttpOnly = true; 
 cookie.Secure = FormsAuthentication.RequireSSL; 
 cookie.Domain = FormsAuthentication.CookieDomain; 
 cookie.Path = FormsAuthentication.FormsCookiePath; 
 if( expiration > 0 ) 
  cookie.Expires = DateTime.Now.AddMinutes(expiration); 
 
 HttpContext context = HttpContext.Current; 
 if( context == null ) 
  throw new InvalidOperationException(); 
 
 // 5. 寫登錄Cookie 
 context.Response.Cookies.Remove(cookie.Name); 
 context.Response.Cookies.Add(cookie); 
} 

這里有必要再補充一下:登錄狀態是有過期限制的。Cookie有 有效期,FormsAuthenticationTicket對象也有 有效期。這二者任何一個過期時,都將導致登錄狀態無效。按照默認設置,FormsAuthenticationModule將采用slidingExpiration=true的策略來處理FormsAuthenticationTicket過期問題。

登錄頁面代碼:

fieldset>legend>包含【用戶信息】的自定義登錄/legend> form action="%= Request.RawUrl %>" method="post"> 
 table border="0"> 
 tr>td>登錄名:/td> 
  td>input type="text" name="loginName" style="width: 200px" value="Fish" />/td>/tr> 
 tr>td>UserId:/td> 
  td>input type="text" name="UserId" style="width: 200px" value="78" />/td>/tr> 
 tr>td>GroupId:/td> 
  td>input type="text" name="GroupId" style="width: 200px" /> 
  1表示管理員用戶 
  /td>/tr> 
 tr>td>用戶全名:/td> 
  td>input type="text" name="UserName" style="width: 200px" value="Fish Li" />/td>/tr> 
 /table>  
 input type="submit" name="CustomizeLogin" value="登錄" /> 
/form>/fieldset> 

登錄處理代碼:

public void CustomizeLogin() 
{ 
 // ----------------------------------------------------------------- 
 // 注意:演示代碼為了簡單,這里不檢查用戶名與密碼是否正確。 
 // ----------------------------------------------------------------- 
 
 string loginName = Request.Form["loginName"]; 
 if( string.IsNullOrEmpty(loginName) ) 
  return; 
 
 
 UserInfo userinfo = new UserInfo(); 
 int.TryParse(Request.Form["UserId"], out userinfo.UserId); 
 int.TryParse(Request.Form["GroupId"], out userinfo.GroupId); 
 userinfo.UserName = Request.Form["UserName"]; 
 
 // 登錄狀態100分鐘內有效 
 MyFormsPrincipalUserInfo>.SignIn(loginName, userinfo, 100); 
 
 TryRedirect(); 
} 

顯示用戶信息的頁面代碼:

fieldset>legend>用戶狀態/legend>form action="%= Request.RawUrl %>" method="post"> 
 % if( Request.IsAuthenticated ) { %> 
  當前用戶已登錄,登錄名:%= Context.User.Identity.Name.HtmlEncode() %> br /> 
   
  % var user = Context.User as MyFormsPrincipalUserInfo>; %> 
  % if( user != null ) { %> 
   %= user.UserData.ToString().HtmlEncode() %> 
  % } %> 
   
  input type="submit" name="Logon" value="退出" /> 
 % } else { %> 
  b>當前用戶還未登錄。/b> 
 % } %>    
/form>/fieldset> 

為了能讓上面的頁面代碼發揮工作,必須在頁面顯示前重新設置HttpContext.User對象。
為此,我在Global.asax中添加了一個事件處理器:

protected void Application_AuthenticateRequest(object sender, EventArgs e) 
{ 
 HttpApplication app = (HttpApplication)sender; 
 MyFormsPrincipalUserInfo>.TrySetUserInfo(app.Context); 
} 
TrySetUserInfo的實現代碼:

/// summary> 
/// 根據HttpContext對象設置用戶標識對象 
/// /summary> 
/// param name="context">/param> 
public static void TrySetUserInfo(HttpContext context) 
{ 
 if( context == null ) 
  throw new ArgumentNullException("context"); 
 
 // 1. 讀登錄Cookie 
 HttpCookie cookie = context.Request.Cookies[FormsAuthentication.FormsCookieName]; 
 if( cookie == null || string.IsNullOrEmpty(cookie.Value) ) 
  return; 
  
 try { 
  TUserData userData = null; 
  // 2. 解密Cookie值,獲取FormsAuthenticationTicket對象 
  FormsAuthenticationTicket ticket = FormsAuthentication.Decrypt(cookie.Value); 
 
  if( ticket != null  string.IsNullOrEmpty(ticket.UserData) == false ) 
   // 3. 還原用戶數據 
   userData = (new JavaScriptSerializer()).DeserializeTUserData>(ticket.UserData); 
 
  if( ticket != null  userData != null ) 
   // 4. 構造我們的MyFormsPrincipal實例,重新給context.User賦值。 
   context.User = new MyFormsPrincipalTUserData>(ticket, userData); 
 } 
 catch { /* 有異常也不要拋出,防止攻擊者試探。 */ } 
} 

在多臺服務器之間使用Forms身份認證

默認情況下,ASP.NET 生成隨機密鑰并將其存儲在本地安全機構 (LSA) 中,因此,當需要在多臺機器之間使用Forms身份認證時,就不能再使用隨機生成密鑰的方式, 需要我們手工指定,保證每臺機器的密鑰是一致的。

用于Forms身份認證的密鑰可以在web.config的machineKey配置節中指定,我們還可以指定加密解密算法:

machineKey 
 decryption="Auto" [Auto | DES | 3DES | AES] 
 decryptionKey="AutoGenerate,IsolateApps" [String] 
/> 

關于這二個屬性,MSDN有如下解釋:

 

在客戶端程序中訪問受限頁面
這一小節送給所有對自動化測試感興趣的朋友。

有時我們需要用代碼訪問某些頁面,比如:希望用代碼測試服務端的響應。

如果是簡單的頁面,或者頁面允許所有客戶端訪問,這樣不會有問題,但是,如果此時我們要訪問的頁面是一個受限頁面,那么就必須也要像人工操作那樣:先訪問登錄頁面,提交登錄數據,獲取服務端生成的登錄Cookie,接下來才能去訪問其它的受限頁面(但要帶上登錄Cookie)。

注意:由于登錄Cookie通常是加密的,且會發生變化,因此直接在代碼中硬編碼指定登錄Cookie會導致代碼難以維護。

在前面的示例中,我已在web.config為MyInfo.aspx設置過禁止匿名訪問,如果我用下面的代碼去調用:

private static readonly string MyInfoPageUrl = "http://localhost:51855/MyInfo.aspx"; 
 
static void Main(string[] args) 
{ 
 // 這個調用得到的結果其實是default.aspx頁面的輸出,并非MyInfo.aspx 
 HttpWebRequest request = MyHttpClient.CreateHttpWebRequest(MyInfoPageUrl); 
 string html = MyHttpClient.GetResponseText(request); 
 
 if( html.IndexOf("span>Fish/span>") > 0 ) 
  Console.WriteLine("調用成功。"); 
 else 
  Console.WriteLine("頁面結果不符合預期。"); 
} 

此時,輸出的結果將會是:

頁面結果不符合預期。

如果我用下面的代碼:

private static readonly string LoginUrl = "http://localhost:51855/default.aspx"; 
private static readonly string MyInfoPageUrl = "http://localhost:51855/MyInfo.aspx"; 
 
static void Main(string[] args) 
{ 
 // 創建一個CookieContainer實例,供多次請求之間共享Cookie 
 CookieContainer cookieContainer = new CookieContainer(); 
 
 // 首先去登錄頁面登錄 
 MyHttpClient.HttpPost(LoginUrl, "NormalLogin=aaloginName=Fish", cookieContainer); 
 
 // 此時cookieContainer已經包含了服務端生成的登錄Cookie 
 
 // 再去訪問要請求的頁面。 
 string html = MyHttpClient.HttpGet(MyInfoPageUrl, cookieContainer); 
 
 if( html.IndexOf("span>Fish/span>") > 0 ) 
  Console.WriteLine("調用成功。"); 
 else 
  Console.WriteLine("頁面結果不符合預期。"); 
 
 // 如果還要訪問其它的受限頁面,可以繼續調用。 
} 

此時,輸出的結果將會是:

調用成功。

說明:在改進的版本中,我首先創建一個CookieContainer實例,它可以在HTTP調用過程中接收服務器產生的Cookie,并能在發送HTTP請求時將已經保存的Cookie再發送給服務端。在創建好CookieContainer實例之后,每次使用HttpWebRequest對象時,只要將CookieContainer實例賦值給HttpWebRequest對象的CookieContainer屬性,即可實現在多次的HTTP調用中Cookie的接收與發送,最終可以模擬瀏覽器的Cookie處理行為,服務端也能正確識別客戶的身份。

ASP.NET Forms身份認證就說到這里,如果您對ASP.NET Windows身份認證有興趣,那么請繼續關注相關文章。

您可能感興趣的文章:
  • 一個簡單的ASP.NET Forms 身份認證的實例方法
  • ASP.NET Forms身份認證

標簽:南寧 崇左 衢州 平涼 汕尾 青海 贛州 洛陽

巨人網絡通訊聲明:本文標題《ASP.NET Forms身份認證詳解》,本文關鍵詞  ASP.NET,Forms,身份認證,詳解,;如發現本文內容存在版權問題,煩請提供相關信息告之我們,我們將及時溝通與處理。本站內容系統采集于網絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《ASP.NET Forms身份認證詳解》相關的同類信息!
  • 本頁收集關于ASP.NET Forms身份認證詳解的相關信息資訊供網民參考!
  • 推薦文章
    婷婷综合国产,91蜜桃婷婷狠狠久久综合9色 ,九九九九九精品,国产综合av
    国产麻豆精品在线观看| 国产乱码精品一区二区三| 亚洲天堂av一区| 日本伊人精品一区二区三区观看方式| 麻豆精品视频在线观看免费| 91小视频免费看| 玉足女爽爽91| 成人av在线资源| 精品国产乱码久久| 国产精品欧美久久久久无广告| 国产精品一二三四五| 欧美一级二级三级蜜桃| 一区二区三区在线观看网站| 午夜精品123| 精品乱码亚洲一区二区不卡| 亚洲va国产天堂va久久en| 久久99精品久久久久婷婷| 久久亚洲精品小早川怜子| 毛片av中文字幕一区二区| 欧美视频一区二区三区| 日韩精品中文字幕在线不卡尤物| 美女一区二区久久| 欧美高清激情brazzers| 亚洲综合网站在线观看| 91啪亚洲精品| 中文字幕免费不卡| 成熟亚洲日本毛茸茸凸凹| 亚洲成人综合视频| 欧美三级乱人伦电影| 欧美成人在线直播| 成人理论电影网| 日韩毛片视频在线看| 国产福利一区二区三区视频 | 欧美精品日韩综合在线| 精品一区二区三区免费毛片爱| 欧美无乱码久久久免费午夜一区 | 91精品久久久久久蜜臀| 日韩和的一区二区| 日韩欧美一级片| 亚洲精品自拍动漫在线| 精品国产一区二区三区久久影院| 国产成人在线电影| 18欧美亚洲精品| 在线观看免费视频综合| 国产精品18久久久久| 中文乱码免费一区二区| 色婷婷狠狠综合| 国产精品综合视频| 亚洲伦理在线精品| 日韩欧美综合在线| 欧美在线综合视频| 国产在线看一区| 久久一区二区视频| 日本电影欧美片| 蜜桃精品视频在线| 97精品国产露脸对白| 日本成人在线看| 中文字幕乱码久久午夜不卡 | 国内精品免费**视频| 亚洲一区二区av在线| 日韩一级黄色大片| 国内精品视频一区二区三区八戒| 亚洲444eee在线观看| 国产精品久久久久久一区二区三区 | 久久夜色精品一区| 国产精品911| 国产精品资源站在线| 午夜国产精品一区| 中文字幕日韩精品一区| 中文字幕乱码一区二区免费| 日韩午夜电影av| 欧美丝袜丝nylons| 欧美色图第一页| 91日韩在线专区| 成人18视频在线播放| 亚洲色大成网站www久久九九| 56国语精品自产拍在线观看| jlzzjlzz欧美大全| 一本一道久久a久久精品| 制服丝袜av成人在线看| 欧美丰满少妇xxxxx高潮对白| 亚洲成人免费视| 国产精品美女久久福利网站| 国产黄色91视频| 免费在线看一区| 久久日一线二线三线suv| 久久超碰97中文字幕| 亚洲欧洲三级电影| 欧美色偷偷大香| 肉丝袜脚交视频一区二区| 日韩精品中文字幕在线不卡尤物| 国产成人精品免费| 蜜臀a∨国产成人精品| 欧美一卡二卡三卡四卡| 6080午夜不卡| 久久精品二区亚洲w码| 国内精品免费在线观看| 免费观看成人鲁鲁鲁鲁鲁视频| 欧美va亚洲va| 亚洲精品高清在线| 不卡一区二区在线| 91高清在线观看| 成人av在线播放网站| 欧美二区三区的天堂| 成人av电影在线| 色综合天天在线| 福利电影一区二区三区| 亚洲三级电影全部在线观看高清| 亚洲一级二级三级在线免费观看| 中文成人av在线| 性做久久久久久免费观看 | 亚洲综合丝袜美腿| 久久久午夜精品| 午夜日韩在线电影| 精品国产露脸精彩对白| 亚洲综合精品久久| 偷拍日韩校园综合在线| 亚洲欧洲日本在线| 亚洲成人av一区二区三区| 青青草原综合久久大伊人精品| 一区二区三区在线视频播放| 午夜精品视频一区| 高清久久久久久| 亚洲一级二级三级在线免费观看| 欧美另类videos死尸| 91 com成人网| 亚洲免费av观看| 国产一区二区三区在线观看免费 | 精品国产一区二区三区四区四 | 国产一区二区视频在线播放| 欧美亚洲国产一区在线观看网站| 欧美经典一区二区| 韩国成人在线视频| 制服丝袜亚洲播放| 亚洲成a人片在线不卡一二三区| 成人免费不卡视频| 国产欧美综合色| 成人一道本在线| 国产婷婷色一区二区三区| 日本不卡不码高清免费观看| 欧美亚洲动漫精品| 一片黄亚洲嫩模| 欧美亚洲国产一卡| 亚洲不卡av一区二区三区| 欧美体内she精高潮| 午夜视频在线观看一区二区| 欧美日韩精品免费观看视频| 亚洲图片自拍偷拍| 欧美在线制服丝袜| 日韩av一级电影| 欧美一级生活片| 国产成人综合精品三级| 国产日本欧洲亚洲| 成人av动漫网站| 亚洲乱码国产乱码精品精的特点| 99视频国产精品| 亚洲综合成人在线| 精品视频免费在线| 麻豆一区二区99久久久久| 久久综合国产精品| 粉嫩嫩av羞羞动漫久久久| 亚洲婷婷综合色高清在线| 在线亚洲+欧美+日本专区| 婷婷一区二区三区| 精品理论电影在线观看| 波多野结衣中文字幕一区二区三区| 亚洲人成网站影音先锋播放| 在线不卡的av| 国产另类ts人妖一区二区| 国产欧美日韩综合精品一区二区| 国产凹凸在线观看一区二区| 亚洲欧美乱综合| 欧美精品乱人伦久久久久久| 免费成人美女在线观看.| 久久久91精品国产一区二区精品 | 午夜久久久久久久久| 久久亚洲综合av| 欧美在线观看视频在线| 久久电影国产免费久久电影| 亚洲欧洲在线观看av| 91精品欧美综合在线观看最新| 国产成人在线电影| 亚洲成人精品一区二区| 国产午夜亚洲精品不卡| 在线观看国产91| 国产乱理伦片在线观看夜一区| 一区二区三区日韩精品视频| 精品国产一区二区三区忘忧草| 色悠悠久久综合| 国产乱码精品一区二区三 | 丝袜美腿亚洲色图| 中文字幕精品一区二区三区精品| 欧美性大战久久久久久久| 国产精一品亚洲二区在线视频| 五月婷婷综合激情| 一区二区三区高清在线| 久久精品亚洲一区二区三区浴池| 欧美人体做爰大胆视频| 北条麻妃国产九九精品视频| 久久国产麻豆精品|