Forms身份認證

引言

大家都知道Http是無狀態的協議,所以訪問一個url,你並不能知道用戶在之前是否已經登陸過。但是許多業務上的邏輯又離不開user的信息,這個時候就可以藉助身份認證來記錄當前user的登錄狀態。這其中Forms身份認證是最常見的身份認證。

這篇博客講的內容就是結合一個最普通的MVC工程來講解下Forms身份認證是怎麼實現記錄用戶登錄狀態的。並說下怎麼自定義一個身份認證。

Forms身份認證

打開Visual Studio 2013,新建Asp.net web mvc application,選擇Internet Application

建好之後,按F5就能看到新建的網站。

修改Index,cshtml:

複製代碼
@if (Request.IsAuthenticated){  <text>用戶已登錄:</text>  @Context.User.Identity.Name }else{  <text>用戶未登錄</text>}
複製代碼

運行之後,你能看到:

11203958-2f288f5d56f5407b995aac457d4c144

繼續修改Index.cshtml

複製代碼
@if (Request.IsAuthenticated){  <text>用戶已登錄:</text>  @Context.User.Identity.Name }else{  <text>用戶未登錄</text> 
    <form method="post" action="@Request.RawUrl">
        <input type="text" name="userName" />
        <input type="submit" value="Submit" />
    </form>}
複製代碼

在對應的HomeController里加上對應的方法:

複製代碼
1[HttpPost]2public ActionResult Index(string id)3{4string userName = Request.Params["userName"];5     FormsAuthentication.SetAuthCookie(userName, true);6return Index();7 }
複製代碼

按F5運行之後,在輸入框內填上user name,運行之後的結果:

11204324-d2003c9e8b14450298b6e74f52b2148

注意上述的結果都是在web.config裏開啓Forms認證的基礎上

<authentication mode="Forms"></authentication>
內部運行機理

Forms身份認證的核心類是FormsAuthenticationModule

它是一個http module,實現了接口IHttpModule,這個接口的主要方法:

Init(HttpApplication Context)

看FomrsAuthenticationModule的實現

複製代碼
 1publicvoid Init(HttpApplication app) 2    { 3if (!_fAuthChecked) 4      { 5                 _fAuthRequired = AuthenticationConfig.Mode == AuthenticationMode.Forms; 6                 _fAuthChecked = true; 7      } 8if (_fAuthRequired) 9      {10        FormsAuthentication.Initialize();11                 app.AuthenticateRequest += new EventHandler(this.OnEnter);12                 app.EndRequest += new EventHandler(this.OnLeave);13      }14         }
複製代碼

解釋下代碼,這個方法首先判斷是否開啓了Forms 認證,如果是Forms認證,就註冊了兩個HttpApplication管道事件AuthenticateRequest和EndRequest,繼續看下

註冊的AuthenticateRequest事件

1 HttpApplication application = (HttpApplication)source;2 HttpContext context = application.Context;3this.OnAuthenticate(new FormsAuthenticationEventArgs(context));

找到了最核心的方法OnAuthenticate

由於此方法比較長,筆者只摘錄出最核心的幾句話

FormsAuthenticationTicket tOld = ExtractTicketFromCookie(e.Context, FormsAuthentication.FormsCookieName, out cookielessTicket);e.Context.SetPrincipalNoDemand(new GenericPrincipal(new FormsIdentity(ticket), newstring[0]));

可以看出最核心的方法是Forms module從http context中的Cookie中解析出FormsAuthenticationTicket對象,然後new出來FormsIdentity,最後傳給HttpContext

module一般都是註冊Application管道事件,在事件裏實現自己的核心處理,比如MVC,也是Module,實現自己的UrlRoutingModule,註冊ResolveRequestCache事件

拓展:Authorization

Authorization可以控制user對網站的訪問權限,比如哪種user可以訪問哪種資源

在上述的工程web.config里加上

<authentication mode="Forms"><forms loginUrl="~/Home/Index"/></authentication><authorization><deny users="?"/></authorization>

deny users="?"代表只有登錄用戶纔可以訪問網站

於此對應的還有allow users="*" 允許任何用戶可以訪問

此類功能是UrlAuthorizationModule 實現,在此不在贅述,實現方式和FormsAuthenticationModule方式大同小異。

自定義身份認證

在某些業務功能裏,需要cookie記住的功能不僅僅是user name,比如remember me功能,這個時候就可以用自定義身份認證來實現。

至於實現方式,既然已經知道Forms身份認證怎麼實現,完全可以比葫蘆畫瓢實現自己需要的認證

下面寫一個簡單的實例

首先仿照FormsAuthenticationModule,註冊AuthenticateRequest事件

複製代碼
 1protectedvoid Application_AuthenticateRequest() 2    { 3             HttpContext context = HttpContext.Current; 4if (!context.Request.RawUrl.Equals("/")) 5      { 6                 HttpCookie cookie = context.Request.Cookies["CustomCookie"]; 7string[] strs = cookie.Value.Split('x'); 8bool rememberMe = Convert.ToInt32(strs[1]) == 1 ? true : false; 9                 HttpContext.Current.User = new CustomPrincipal(new CustomIdentity(strs[0], rememberMe));10      }11         }
複製代碼

標註:

context.Request.RawUrl.Equals("/") 此句是簡單的將Index.cshtml作爲登錄頁面,實際應用中當然需要修改下。

其中CustomPrincipal和CustomIdentity類的代碼如下

複製代碼
 1publicclass CustomPrincipal : IPrincipal 2  { 3private IIdentity identity; 4 5public CustomPrincipal(IIdentity identity) 6    { 7this.identity = identity; 8    } 910public IIdentity Identity11    {12get { return identity; }13    }1415publicbool IsInRole(string role)16    {17thrownew NotImplementedException();18    }19  }2021publicclass CustomIdentity : IIdentity22  {2324privatestring name;25privatebool rememberMe;2627public CustomIdentity(string name, bool rememberMe)28    {29this.name = name;30this.rememberMe = rememberMe;31    }3233publicstring AuthenticationType34    {35get { returnstring.Empty; }36    }3738publicbool IsAuthenticated39    {40get { returntrue; }41    }4243publicstring Name44    {45get { return name; }46    }47     }
複製代碼

Index.cshtml繼續作爲登錄頁面,對應的登錄方法:

複製代碼
 1    [HttpPost] 2public ActionResult Index(string id) 3    { 4string userName = Request.Params["userName"]; 5             HttpCookie cookie = new HttpCookie("CustomCookie"); 6             cookie.Value = userName + "x" + 1.ToString(); 7             cookie.Expires = DateTime.Now.AddHours(1); 8      System.Web.HttpContext.Current.Response.SetCookie(cookie); 9return RedirectToAction("About");10         }
複製代碼

About.cshtml作爲判斷是否登錄的頁面

複製代碼
1@if (Request.IsAuthenticated)2{3     <text>用戶已登錄:</text>
4  @Context.User.Identity.Name 5}6else7{8     <text>用戶未登錄</text> 
9 }
複製代碼

最後運行結果:

11215927-94fe13ca5a7b487c88222e9c5bedf2e

到此,整篇博客已結束,許多地方說的還是比較簡單抽象,望理解。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章