VS2013中web項目中自動生成的ASP.NET Identity代碼思考

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節設置。

image

我們再分析IdentityDbContext類的元數據。

image

可以看出它繼承自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方法創建用戶。

類元數據如下圖,裏面封裝了各種用戶操作的方法。

image

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);
        }
複製代碼

代碼比較類似,用UserManagerFindAsync查找用戶,成功又有是用調用SignInAsync方法進行登錄(方法內部是用AuthenticationManager的SignIn進行登錄)

四、總結

這裏用到的是ApplicationDbContext、UserStore、UserManager、AuthenticationManager、ApplicationUser這5個類。其中ApplicationDbContext需要設置連接字符串,ApplicationUser可以擴展用戶字段,UserStore、UserManager、AuthenticationManager這三個類都封裝好的類直接拿來用就行,關鍵是要理解方法和屬性的含義、清楚其用途。

五、問題及思考

  • 依靠[Authorize]應該是用來驗證用戶是否登錄的,直接用來進行權限管理(控制器上寫[Authorize(Roles="管理員")])是不是太僵化了?
  • Claims這是東西什麼東西,從來沒用過,是否與權限有關?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章