ASP.NET MVC3 Custom FormAuthorize

我們開發web系統,用戶身份驗證是最常見不過的。最簡單的辦法就是定一個基類,基類裏面有判斷Cookie或Session是否存在,然後決定是否跳轉。今天就利用MVC的特性來一個不一樣的驗證方式。

 

  1. public class CustomAuthorizeAttribute : AuthorizeAttribute  
  2. {  
  3.     protected override bool AuthorizeCore(HttpContextBase httpContext)  
  4.     {  
  5.         UserIdentity user = WebUtility.GetIdentity(httpContext);  
  6.         if (user != null)  
  7.         {  
  8.             httpContext.User = new UserPrincipal(WebUtility.GetIdentity(httpContext));  
  9.         }  
  10.   
  11.         return user != null;  
  12.     }  
  13.   
  14. }  

這是自定義的驗證機制,重寫了系統自帶的驗證核心邏輯,下面是系統自帶的邏輯:

  1. protected virtual bool AuthorizeCore(HttpContextBase httpContext)  
  2. {  
  3.     if (httpContext == null)  
  4.     {  
  5.         throw new ArgumentNullException("httpContext");  
  6.     }  
  7.     IPrincipal user = httpContext.User;  
  8.     return user.Identity.IsAuthenticated && (this._usersSplit.Length <= 0 || this._usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase)) && (this._rolesSplit.Length <= 0 || this._rolesSplit.Any(new Func<stringbool>(user.IsInRole)));  
  9. }  

可以看出,驗證都是用httpContext.User屬性來判斷,所以我們對登錄、退出和獲取用戶信息都通過httpContext.User屬性就行了。從MVC的示例中我們也可以發現確實如此,看看MVC3的Views裏的_LogOnPartial.cshtml的代碼:

  1. @if(Request.IsAuthenticated) {  
  2.     <text>Welcome <strong>@User.Identity.Name</strong>!  
  3.     [ @Html.ActionLink("Log Off", "LogOff", "Account") ]</text>  
  4. }  
  5. else {  
  6.     @:[ @Html.ActionLink("Log On", "LogOn", "Account") ]  
  7. }  

那麼現在再看我們剛纔重寫的驗證邏輯,就大致明白了。首先通過一個外部的邏輯獲取User,如果沒有User說明沒有登錄,否則就給User賦值,標記登錄了。
再看我們是如何獲取這個User的

public static UserIdentity GetIdentity(HttpContextBase context)
{
	var user = GetUser(context);
	return new UserIdentity(user);
}

public static User GetUser(HttpContextBase httpContext)  
{  
    var user = httpContext.Request.Cookies[COOKIE_NAME_KEY];  //明文 比如用的userId
    var info = httpContext.Request.Cookies[COOKIE_INFO_KEY];  //密文
    if (user == null || info == null || !info.Value.DESDecrypt(DES_KEY).Contains(user.Value))  //解密後不一致,說明無效
    {  
        return null;  
    }  
  
    return new User{UserID = int.parse(user.value)};
}  


這就很易懂了,也是通過的cookie對用戶資料做的存儲。
httpContext.User是IPrincipal類型,所以我們要自定義一個UserPrincipal類:

 

public class UserPrincipal : IPrincipal
{
	public IIdentity Identity { get; private set; }

	public UserPrincipal(IIdentity identity)
	{
		this.Identity = identity;
	}

	public bool IsInRole(string role)
	{
		throw new NotSupportedException();
	}
}

似乎UserPrincipal並沒有什麼內容,而真證的數據存在Identity屬性裏,所以還需要定義一個UserIdentity類,來滿足UserPrincipal。所以從最上面的代碼可以看出WebUtility.GetIdentity(httpContext)返回的就是一個UserIdentity實例。

  1. public class UserIdentity : IIdentity  
  2. {  
  3.     public UserIdentity(User user)//此User就是從Cookie解密出來實例化的User
  4.     {  
  5.         User = user;  
  6.     }  
  7.   
  8.     public User User { getprivate set; }  
  9.   
  10.     public string Name  
  11.     {  
  12.         get { return User == null ? string.Empty : User.UserName; }  
  13.     }  
  14.   
  15.     public string AuthenticationType  
  16.     {  
  17.         get { return "maddemon"; }  
  18.     }  
  19.   
  20.     public bool IsAuthenticated  
  21.     {  
  22.         get { return User != null; }  
  23.     }  
  24. }  

好了,整個驗證核心需要的東西我們都準備好了,還差一個最重要的東西就是我們登錄的時候需要寫cookie,退出的時候需要清除cookie。

  1. public static void SetAuthorizeCookie(HttpContextBase httpContext, User user)  
  2. {  
  3.     httpContext.Response.SetCookie(new HttpCookie(COOKIE_NAME_KEY, user.UserName));  
  4.     httpContext.Response.SetCookie(new HttpCookie(COOKIE_INFO_KEY, (user.UserName + "|" + user.Role).DESEncrypt(DES_KEY)));  
  5. }  
  6.   
  7. public static void RemoveAuthorizeCookie(HttpContextBase httpContext)  
  8. {  
  9.     httpContext.Response.SetCookie(new HttpCookie(COOKIE_NAME_KEY, null) { Expires = DateTime.Now.AddDays(-1) });  
  10.     httpContext.Response.SetCookie(new HttpCookie(COOKIE_INFO_KEY, null) { Expires = DateTime.Now.AddDays(-1) });  
  11. }  

這樣一套下來,需要登錄的Controller或Action,我們只需要加上一個[CustomAuthorize]屬性就可以,如果沒有登錄就會自動跳轉到config裏配置的登錄頁。是不是很不方便啊? 方便嗎?不方便嗎? 方便嗎?不方便嗎? 方便嗎?不方便嗎?是不是啊 哈哈

  1. <authentication mode="Forms">  
  2.   <forms loginUrl="~/Account/LogOn" timeout="2880" />  
  3. </authentication>  
發佈了42 篇原創文章 · 獲贊 191 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章