在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为咱们自定义的对象。
若有不对,请多多指教。
参考: