vs2013沒有再分webform、mvc、api項目,使用vs2013創建一個web項目模板選MVC,身份驗證選個人用戶賬戶。項目會生成ASP.NET Identity的一些代碼。這些代碼主要在AccountController中。
我的感興趣的主要是幾個方面。1、可以擴展用戶類的字段;2、使用EF codefirst存儲;3、OWIN集成 。4、基於Claims
我看了整個代碼覺得它有點類似於三層架構。這裏要特別重要的幾個類:
- ApplicationDbContext類。就是繼承字自ef的DbContext,EF codefirst的必備。
- UserStore類。封裝了數據庫訪問一系列方法,通過DbContext操作數據庫。類似三層架構中的數據訪問層。
- UserManager類。用戶管理的類,封裝了用戶、角色、Claim等一系列的方法,通過UserStore類與數據庫交互。類似三層的業務邏輯層。
- AuthenticationManager類。Owin的一些東西,包含了用戶登錄、註銷、驗證等一些方法。
- ApplicationUser。用戶模型。繼承自IdentityUser,可以自行擴展屬性。
這個東西怎麼用呢:
一、設置ApplicationDbContext類
先看下代碼:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> { public ApplicationDbContext() : base("DefaultConnection") { } }
這個類繼承自IdentityDbContext類,ApplicationUser是用戶模型。
這裏要設置的就是base("DefaultConnection")。數據庫的鏈接字符串。在web.config的connectionStrings節設置。
我們再分析IdentityDbContext類的元數據。
可以看出它繼承自DbContext。共再數據庫創建兩個表,Users根據傳輸的用戶模型來創建。角色表 Roles表根據IdentityRole來創建(這個類只有兩個屬性ID和name)看以看出這裏角色的字段是不能擴展的。
二、擴展用戶模型(ApplicationUser)
當然不擴展可以直接用,當預設不滿足要求時就可以自行擴展字段。首先看下代碼
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more. public class ApplicationUser : IdentityUser { }
是個繼承自IdentityUser的空類,需要擴展的字段直接寫這就行,如果擴展個年齡(Age)
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more. public class ApplicationUser : IdentityUser { public int Age { get; set; } }
我們看下其繼承的IdentityUser類,這裏面都是一些預知的屬性。IUser是一個接口只有ID和username兩個屬性。
public class IdentityUser : IUser { public IdentityUser(); public IdentityUser(string userName); public virtual ICollection<IdentityUserClaim> Claims { get; } public virtual string Id { get; set; } public virtual ICollection<IdentityUserLogin> Logins { get; } public virtual string PasswordHash { get; set; } public virtual ICollection<IdentityUserRole> Roles { get; } public virtual string SecurityStamp { get; set; } public virtual string UserName { get; set; } }
三、利用UserManager,AuthenticationManager進行用戶相關操作
這一步就可直接在控制器中寫代碼了。我們先看下AccountController的構造函數
[Authorize] public class AccountController : Controller { public AccountController() : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()))) { }
注意,UserManager,ApplicationUser,UserStore,ApplicationDbContext。
1、看下用戶註冊代碼
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Register(RegisterViewModel model) { if (ModelState.IsValid) { var user = new ApplicationUser() { UserName = model.UserName }; var result = await UserManager.CreateAsync(user, model.Password); if (result.Succeeded) { await SignInAsync(user, isPersistent: false); return RedirectToAction("Index", "Home"); } else { AddErrors(result);//一個循環向ModelState添加錯誤消息的方法 } } // 如果我們進行到這一步時某個地方出錯,則重新顯示錶單 return View(model); }
我們來看下這段代碼。
public async Task<ActionResult> Register(RegisterViewModel model)這句將Register聲明爲異步action。
var user = new ApplicationUser() { UserName = model.UserName };這句是ApplicationUser這個創建模型類。
var result = await UserManager.CreateAsync(user, model.Password);
這句是一個異步添加用戶。利用UserManager這個類的CreateAsync方法創建用戶。
類元數據如下圖,裏面封裝了各種用戶操作的方法。
await SignInAsync(user, isPersistent: false);這句依然。這裏是AccountController的一個函數,代碼如下:
private async Task SignInAsync(ApplicationUser user, bool isPersistent) { AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie); var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie); AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity); }
可以看出利用AuthenticationManager的SignOut和SignIn進行清除cookie和登錄。
2、再看下注銷
[HttpPost] [ValidateAntiForgeryToken] public ActionResult LogOff() { AuthenticationManager.SignOut(); return RedirectToAction("Index", "Home"); }
使用AuthenticationManager.SignOut方法。這是類似於 WebFor中的FormsAuthentication所使用的FormsAuthentication.SignOut方法。
3、再看登錄代碼
[HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginViewModel model, string returnUrl) { if (ModelState.IsValid) { var user = await UserManager.FindAsync(model.UserName, model.Password); if (user != null) { await SignInAsync(user, model.RememberMe); return RedirectToLocal(returnUrl); } else { ModelState.AddModelError("", "Invalid username or password."); } } // 如果我們進行到這一步時某個地方出錯,則重新顯示錶單 return View(model); }
代碼比較類似,用UserManager的FindAsync查找用戶,成功又有是用調用SignInAsync方法進行登錄(方法內部是用AuthenticationManager的SignIn進行登錄)
四、總結
這裏用到的是ApplicationDbContext、UserStore、UserManager、AuthenticationManager、ApplicationUser這5個類。其中ApplicationDbContext需要設置連接字符串,ApplicationUser可以擴展用戶字段,UserStore、UserManager、AuthenticationManager這三個類都封裝好的類直接拿來用就行,關鍵是要理解方法和屬性的含義、清楚其用途。
五、問題及思考
- 依靠[Authorize]應該是用來驗證用戶是否登錄的,直接用來進行權限管理(控制器上寫[Authorize(Roles="管理員")])是不是太僵化了?
- Claims這是東西什麼東西,從來沒用過,是否與權限有關?