在MVC中對於須要登陸才能夠訪問的頁面,只須要在對應的Controller或Action上添加特性[Authorize]
就能夠限制非登陸用戶訪問該頁面。那麼若是實現登陸?html
HTTP協議是無狀態的。因此上一次請求和下一次請求並不能相互關聯起來,就是說這些請求並不能肯定是哪一個用戶和用戶的狀態。可是對於登陸來講,咱們就須要準確的知道用戶的狀態及是哪一個用戶。web
一般有兩種狀況來記錄用戶狀態。瀏覽器
一種在服務端經過Session來標識。安全
一種經過Cookie在客戶端標識用戶。(用戶每次請求應用程序時都會攜帶該Cookie。)cookie
Forms 身份驗證將身份驗證標記保留在 Cookie 或頁的 URL 中。Forms 身份驗證經過 FormsAuthenticationModule 類參與到 ASP.NET 頁面的生命週期中。能夠經過 FormsAuthentication 類訪問 Forms 身份驗證信息和功能。mvc
在Web.Config
配置文件中指定驗證的方式爲Form
,並設置跳轉的登陸地址和Cookie的名稱,及超時時間等。asp.net
<system.web> <authentication mode="Forms"> <forms loginUrl="/Home/Login" timeout="120" cookieless="UseCookies" name="LoginCookieName"></forms> </authentication> </system.web>
設置該配置文件,並不須要特地給Action傳遞returnUrl,就能夠獲取到跳轉地址。less
Form特性的詳細說明ide
此時當未登陸的用戶訪問有[Authorize]特性的Action操做時,會被跳轉到Login頁面,同時Login頁面的URL後面會添加一個加密的ReturnUrl地址,該地址指向以前訪問的有[Authorize]特性的Action地址。ui
以前提到Form認證其實就是生成了一個Cookie,存放到用戶的瀏覽器中。經過FormAuthenication.SetAuthCookie(userName,true);
設置驗證登陸的Cookie。再經過頁面跳轉將Cookie響應給客戶端。
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Login(LoginViewModel vm,string returnUrl) { if (ModelState.IsValid) { //用戶名,密碼驗證 FormsAuthentication.SetAuthCookie(vm.UserName, true); //設置Form驗證Cookie,後一個 參數爲是否建立持久Cookie。及true爲能夠在用戶瀏覽器上保存的。false爲不在瀏覽器上保存。 if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } return RedirectToAction("Detail"); } else return View(vm); }
此時當咱們登陸之後會在瀏覽器中生成一個跟配置文件中名稱相同的Cookie
如:
該Cookie就是咱們已經登陸,經過Form驗證的憑證。
此時咱們就能夠訪問那些須要登陸才能訪問的頁面。
刪除對應的Cookie便可實現註銷,代碼以下:
[Authorize] public ActionResult LogOut() { FormsAuthentication.SignOut();//經過Forms驗證來刪除Cookie return View(); }
有些頁面可能只容許特定用戶才能訪問,在MVC中能夠經過[Authorize(Roles="VIP")]
設置Action或Controller,表示只有角色爲VIP的用戶才能夠訪問該頁面。
如:只有登陸且用戶角色爲VIP的才能夠訪問這個頁面。
[Authorize(Roles = "VIP")] public ActionResult VIP() { return View(); }
同時
須要在設置Form驗證憑據時把用戶角色添加到Cookie。
如:
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Login(LoginViewModel vm, string returnUrl) { if (ModelState.IsValid) { //用戶名,密碼驗證 //FormsAuthentication.SetAuthCookie(vm.UserName, true); //設置Form驗證Cookie,後一個 參數爲是否建立持久Cookie。及true爲能夠在用戶瀏覽器上保存的。false爲不在瀏覽器上保存。 //if (Url.IsLocalUrl(returnUrl)) //{ // return Redirect(returnUrl); //} //return RedirectToAction("Detail"); vm.Role = "VIP"; var authTicket = new FormsAuthenticationTicket( 1, // 版本 vm.UserName, // 用戶名稱 DateTime.Now, // 建立日期 DateTime.Now.AddMinutes(20), // 過時時間 vm.Remember, // 是否記住 vm.Role // 用戶角色 ); string encryptedTicket = FormsAuthentication.Encrypt(authTicket); var authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket); authCookie.HttpOnly = true;//客戶端腳本不能訪問 authCookie.Secure = FormsAuthentication.RequireSSL;//是否僅用https傳遞cookie authCookie.Domain = FormsAuthentication.CookieDomain;//與cookie關聯的域 authCookie.Path = FormsAuthentication.FormsCookiePath;//cookie關聯的虛擬路徑 Response.Cookies.Add(authCookie); if (Url.IsLocalUrl(returnUrl)) { return Redirect(returnUrl); } return RedirectToAction("Detail"); } else return View(vm); }
在Global.asax.cs文件中重寫Application_AuthenticateRequest
事件
protected void Application_AuthenticateRequest(Object sender, EventArgs e) { //獲取Cookie HttpCookie authCookie = Context.Request.Cookies[FormsAuthentication.FormsCookieName]; if (authCookie == null || authCookie.Value == "") return; FormsAuthenticationTicket authTicket; try { //解析Cookie authTicket = FormsAuthentication.Decrypt(authCookie.Value); } catch { return; } // 解析權限 string[] roles = authTicket.UserData.Split(';'); if (Context.User != null) //把權限賦值給當前用戶 Context.User = new GenericPrincipal(Context.User.Identity, roles); }
至此最簡單的Form身份驗證明現了。可是該Cookie只包含了用戶名,沒有其餘信息。若是要包含其餘信息,能夠經過擴展用戶的身份標識(HttpContext.User實例)來實現。
HttpContext.User定義以下
經過User屬性能夠訪問Iprincipal接口的屬性和方法。
//獲取或設置當前 HTTP 請求的安全信息。 public IPrincipal User { get; [SecurityPermissionAttribute(SecurityAction.Demand, ControlPrincipal = true)] set; }
因此擴展用戶身份標識能夠經過實現IPrincipal接口。
如:
public class MyFormsPrincipal<TUserData> : 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);//Forms身份驗證 _userData = userData; } public TUserData UserData { get { return _userData; } } public IIdentity Identity { get { return _identity; } } public bool IsInRole(string role) { return false; } }
這個方法的核心是:
- 在登陸時,建立自定義的FormsAuthenticationTicket對象,它包含了用戶信息。
- 加密FormsAuthenticationTicket對象。
- 建立登陸Cookie,它將包含FormsAuthenticationTicket對象加密後的結果。
- 在管線的早期階段,讀取登陸Cookie,若是有,則解密。
- 從解密後的FormsAuthenticationTicket對象中還原咱們保存的用戶信息。
- 設置HttpContext.User爲咱們自定義的對象。
若有不對,請多多指教。
參考: