ASP.NET MVC 3數據庫
使用Forms身份驗證cookie
身份驗證流程ide
1、用戶登陸函數
一、驗證表單:ModelState.IsValidui
二、驗證用戶名和密碼:經過查詢數據庫驗證加密
三、若是用戶名和密碼正確,則在客戶端保存Cookie以保存用戶登陸狀態:SetAuthCookiespa
1):從數據庫中查出用戶名和一些必要的信息,並把額外信息保存到UserData中code
2):把用戶名和UserData保存到 FormsAuthenticationTicket 票據中orm
3):對票據進行加密 Encrypt對象
4):將加密後的票據保存到Cookie發送到客戶端
四、跳轉到登陸前的頁面
2、驗證登陸
一、在Global中註冊PostAuthenticateRequest事件函數,用於解析客戶端發過來的Cookie數據
1):經過 HttpContext.Current.User.Identity 判斷用戶是否登陸(FormsIdentity,IsAuthenticated,AuthenticationType)
2):從HttpContext 的Request的Cookie中解析出Value,解密獲得 FormsAuthenticationTicket 獲得UserData
二、角色驗證
在Action加入 Authorize特性,能夠進行角色驗證
在 HttpContext.Current.User 的 IsInRole 方法進行角色認證(須要重寫)
下面是代碼,以上用到的全部驗證的類都進行重載
1、首先是用戶用戶身份認證的 IPrincipal
這裏抽象出通用屬性,定義兩個 IPrincipal
//通用的用戶實體 public class MyFormsPrincipal<TUserData> : IPrincipal where TUserData : class, new() { //當前用戶實例 public IIdentity Identity { get; private set; } //用戶數據 public TUserData UserData { get; private set; } 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 bool IsInRole(string role) { var userData = UserData as MyUserDataPrincipal; if (userData == null) throw new NotImplementedException(); return userData.IsInRole(role); } //用戶名驗證 public bool IsInUser(string user) { var userData = UserData as MyUserDataPrincipal; if (userData == null) throw new NotImplementedException(); return userData.IsInUser(user); } }
通用實體裏面能夠存放數據實體,而且把角色驗證和用戶驗證放到了具體的數據實體裏面
//存放數據的用戶實體 public class MyUserDataPrincipal : IPrincipal { //數據源 private readonly MingshiEntities mingshiDb = new MingshiEntities(); public int UserId { get; set; } //這裏能夠定義其餘一些屬性 public List<int> RoleId { get; set; } //當使用Authorize特性時,會調用改方法驗證角色 public bool IsInRole(string role) { //找出用戶全部所屬角色 var userroles = mingshiDb.UserRole.Where(u => u.UserId == UserId).Select(u => u.Role.RoleName).ToList(); var roles = role.Split(new[] {','}, StringSplitOptions.RemoveEmptyEntries); return (from s in roles from userrole in userroles where s.Equals(userrole) select s).Any(); } //驗證用戶信息 public bool IsInUser(string user) { //找出用戶全部所屬角色 var users = user.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); return mingshiDb.User.Any(u => users.Contains(u.UserName)); } [ScriptIgnore] //在序列化的時候忽略該屬性 public IIdentity Identity { get { throw new NotImplementedException(); } } }
2、用於驗證和設置Cookie的 FormsAuthentication
//身份驗證類 public class MyFormsAuthentication<TUserData> where TUserData : class, new() { //Cookie保存是時間 private const int CookieSaveDays = 14; //用戶登陸成功時設置Cookie public static void SetAuthCookie(string username, TUserData userData, bool rememberMe) { if (userData == null) throw new ArgumentNullException("userData"); var data = (new JavaScriptSerializer()).Serialize(userData); //建立ticket var ticket = new FormsAuthenticationTicket( 2, username, DateTime.Now, DateTime.Now.AddDays(CookieSaveDays), rememberMe, data); //加密ticket var cookieValue = FormsAuthentication.Encrypt(ticket); //建立Cookie var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, cookieValue) { HttpOnly = true, Secure = FormsAuthentication.RequireSSL, Domain = FormsAuthentication.CookieDomain, Path = FormsAuthentication.FormsCookiePath, }; if (rememberMe) cookie.Expires = DateTime.Now.AddDays(CookieSaveDays); //寫入Cookie HttpContext.Current.Response.Cookies.Remove(cookie.Name); HttpContext.Current.Response.Cookies.Add(cookie); } //從Request中解析出Ticket,UserData public static MyFormsPrincipal<TUserData> TryParsePrincipal(HttpRequest request) { if (request == null) throw new ArgumentNullException("request"); // 1. 讀登陸Cookie var cookie = request.Cookies[FormsAuthentication.FormsCookieName]; if (cookie == null || string.IsNullOrEmpty(cookie.Value)) return null; try { // 2. 解密Cookie值,獲取FormsAuthenticationTicket對象 var ticket = FormsAuthentication.Decrypt(cookie.Value); if (ticket != null && !string.IsNullOrEmpty(ticket.UserData)) { var userData = (new JavaScriptSerializer()).Deserialize<TUserData>(ticket.UserData); if (userData != null) { return new MyFormsPrincipal<TUserData>(ticket, userData); } } return null; } catch { /* 有異常也不要拋出,防止攻擊者試探。 */ return null; } } }
3、用於驗證角色和用戶名的Authorize特性
//驗證角色和用戶名的類 public class MyAuthorizeAttribute : AuthorizeAttribute { protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext) { var user = httpContext.User as MyFormsPrincipal<MyUserDataPrincipal>; if (user != null) return (user.IsInRole(Roles) || user.IsInUser(Users)); return false; } protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { //驗證不經過,直接跳轉到相應頁面,注意:若是不使用如下跳轉,則會繼續執行Action方法 filterContext.Result = new RedirectResult("http://www.baidu.com"); } }
好了,四個類定義完成,接下來是使用
一、首先是登錄
[HttpPost] public ActionResult LogOn(LogOnModel model, string returnUrl) { if (ModelState.IsValid) {
//經過數據庫查詢驗證 var bll= new UserBll(); var userId = bll.Validate(model.UserName, model.Password, HttpContext.Request.UserHostAddress, HttpContext.Request.UserAgent); if (userId > 0) { //驗證成功,用戶名密碼正確,構造用戶數據(能夠添加更多數據,這裏只保存用戶Id) var userData = new MyUserDataPrincipal {UserId = userId}; //保存Cookie MyFormsAuthentication<MyUserDataPrincipal>.SetAuthCookie(model.UserName, userData, model.RememberMe); if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\")) { return Redirect(returnUrl); } else { return RedirectToAction("Index", "Home"); } } else { ModelState.AddModelError("", "提供的用戶名或密碼不正確。"); } } // 若是咱們進行到這一步時某個地方出錯,則從新顯示錶單 return View(model); }
2、登錄完成後,是驗證,驗證以前首先要得到客戶端的用戶數據(從以前設置的Cookie中解析)
在全局文件:Global.asax 中添加下面代碼
protected void Application_PostAuthenticateRequest(object sender, System.EventArgs e) { var formsIdentity = HttpContext.Current.User.Identity as FormsIdentity; if (formsIdentity != null && formsIdentity.IsAuthenticated && formsIdentity.AuthenticationType == "Forms") { HttpContext.Current.User = MyFormsAuthentication<MyUserDataPrincipal>.TryParsePrincipal(HttpContext.Current.Request); } }
這樣就從Request解析出了UserData,下面能夠用於驗證了
3、在須要驗證角色的Action上添加 [MyAuthorize] 特性
[MyAuthorize(Roles = "User", Users = "bomo,toroto")] public ActionResult About() { return View(); }
當用戶訪問該Action時,調用 MyAuthorize 的 AuthorizeCore 方法進行驗證, 若是驗證成功,則繼續執行,若是驗證失敗,會調用 HandleUnauthorizedRequest方法作相應處理,在MyAuthorize 中能夠得到這裏定義的 Roles 和 Users 進行驗證