1. 什麼是 ASP.NET Identity
ASP.NET Identity 是微軟推出,用於在ASP.NET應用中系統管理使用者的組件。
The mainstay for user management in recent years has been ASP.NET Membership, which has suffered from design choices. The biggest limitation is that the schema used to store the data worked only with SQL Server and was difficult to extend without re-implementing a lot of provider classes. The schema itself was overly complex, which made it harder to implement changes than it should have been.
--Pro ASP.NET MVC 5 Platform
2. 如何配置ASP.NET Identity with MySQL 2.1 配置ASP.NET Identity 2.1.1 安裝相應的組件包
Microsoft.AspNet.Identity.EntityFramework
Microsoft.AspNet.Identity.OWIN
Microsoft.Owin.Host.SystemWeb
2.1.2 自訂核心組件
$ User model
預設的user model是 IdentityUser(Microsoft.AspNet.Identity.EntityFramework)。這個類有12個內建的屬性,如 Id、UserName、PasswordHash、Email等
一般,根據業務需求,我們需要其它額外的屬性。我們可以建立一個繼承自IdentityUser的自訂類,在這個自訂類中添加額外的屬性。
using Microsoft.AspNet.Identity.EntityFrameworkpublic class AppUser : IdentityUser { // 在這裡添加額外的屬性}
$ DB Context
一般我們需要改變Identity用到的資料庫表的名稱。預設的資料庫表為:AspNetUsers、AspNetUserRoles、AspNetUserLogins、AspNetUserCliams、AspNetRoles。
using System.Data.Entity;using Microsoft.Asp.Net.Identity.EntityFramework;public class AppIdentityDbContext : IdentityDbContext<AppUser> { public AppIdentityDbContext() : base("IdentityDb") { } public AppIdentityDbContext(string connectionString) : base(connectionString) { } protected override void OnModelCreating(DbModelBuilder modelBuilder { base.OnModelCreating(modelBuilder); modelBuilder.Entity<AppUser>().ToTable("user"); modelBuilder.Entity<IdentityRole>().ToTable("role"); modelBuilder.Entity<IdentityUserRole>().ToTable("userrole"); modelBuilder.Entity<IdentityUserClaim>().ToTable("userclaim"); modelBuilder.Entity<IdentituUserLogin>().ToTable("userlogin"); }}
$ DB 初始化
如果你不熟悉Identity的資料庫表的結構,可以通過代碼讓Identity自動建立。
如果你比較熟悉,那我推薦用專業的資料庫管理工具來建立,如MySQL Workbench。
程式碼範例。一般初始化代碼只需要執行一次,好好斟酌策略,防止資料被刪。
using System.Data.Entity;public class AppIdentityDbContext : IdentityDbContext<AppUser> { ... static AppIdentityDbContext() { Database.SetInitializer<AppIdentityDbContext>(new IdentityDbInit()); }}
using System.Data.Entity;using Microsoft.AspNet.Identity;using Microsoft.AspNet.Identity.EntityFramework;public class IdentityDbInit : DropCreateDatabaseAlways<AppIdentityDbContext> { protectedd override void Seed(AppIdentityDbContext context) { this.InitAdmin(context); base.Seed(context); } public void InitAdmin(AppIdentityDbContext context) { string adminName = "admin"; string adminPassword = "changeme"; string adminRoleName = "Administrators"; // 建立使用者 UserManager<AppUser> userManager = new UserManager<AppUser>( new UserStore<AppUser>(context)); var user = new AppUser { UserName = adminName }; userManager.Create(user, adminPassword); // 建立角色 RoleManager<IdentityRole> roleManager = new RoleManager<IdentityRole>( new RoleStore<IdentityRole>(context)); var adminRole = roleManager.Create(new IdentityRole(adminRoleName)); // 給使用者賦予角色 userManager.AddToRole(user.Id, adminRoleName); }}
$ 配置
using Microsoft.AspNet.Identity;using Microsoft.AspNet.Identity.EntityFramework;using Microsoft.AspNet.Identity.Owin;using Microsoft.Owin;using Microsoft.Owin.Security.Cookies;using Owin;public class IdentityConfig { public void Configuration(IAppBuilder app) { app.CreatePerOwinContext<AppIdentityDbContext>(() => new AppIdentityDbContext()); app.CreatePerOwinContext<UserManager<AppUser>>( (o, c) => new UserManager<AppUser>(new UserStore<AppUser>( c.Get<AppIdentityDbContext>()))); app.CreatePerOwinContext<RoleManager<IdentityRole>>( (o, c) => new RoleManager<IdentityRole>(new RoleStore<IdentityRole>( c.Get<AppIdentityDbContext>()))); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login") }); }}
2.1.3 配置web.config
<configuration> <appSettings> <add key="owin:AppStartup" value="IdentityConfig" /> ... </appSettings> ...</configuration>
2.2 配置MySQL DB 2.2.1 安裝相應的組件包
MySql.Data.Entity
2.2.2 配置web.config
<configuration> <configSections> <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework"> </configSections> <system.data> <DbProviderFactories> <remove invariant="MySql.Data.MySqlClient" /> <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data" /></DbProviderFactories> </system.data> <connectionStrings> <add name="IdentityDb" connectionString="server=192.168.0.9;user id=tester;password=changeme;database=IdentityDb" providerName="MySql.Data.MySqlClient" /> </connectionStrings> <entityFramework> <providers> <provider invariantName="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlProviderServices, MySql.Data.Entity.EF6" /></providers> </entityFramework></configuration>
2.2.3 建立DB
方法一:建立一個沒有表的空DB,通過代碼讓Identity自動建立表。(見上文)
方法二:建立一個帶有所有Identity相關表的DB
$ User
CREATE TABLE `user` ( `Id` varchar(128) NOT NULL, `Email` varchar(256) DEFAULT NULL, `EmailConfirmed` tinyint(1) NOT NULL, `PasswordHash` longtext, `SecurityStamp` longtext, `PhoneNumber` longtext, `PhoneNumberConfirmed` tinyint(1) NOT NULL, `TwoFactorEnabled` tinyint(1) NOT NULL, `LockoutEndDateUtc` datetime DEFAULT NULL, `LockoutEnabled` tinyint(1) NOT NULL, `AccessFailedCount` int(11) NOT NULL, `UserName` varchar(256) NOT NULL, PRIMARY KEY (`Id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8
$ Role
CREATE TABLE `role` ( `Id` varchar(128) NOT NULL, `Name` varchar(256) NOT NULL, PRIMARY KEY (`Id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8
$ UserRole
CREATE TABLE `userrole` ( `UserId` varchar(128) NOT NULL, `RoleId` varchar(128) NOT NULL, PRIMARY KEY (`UserId`,`RoleId`), KEY `IdentityRole_Users` (`RoleId`), CONSTRAINT `AppUser_Roles` FOREIGN KEY (`UserId`) REFERENCES `user` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION, CONSTRAINT `IdentityRole_Users` FOREIGN KEY (`RoleId`) REFERENCES `role` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION) ENGINE=InnoDB DEFAULT CHARSET=utf8
$ UserClaim
CREATE TABLE `userclaim` ( `Id` int(11) NOT NULL AUTO_INCREMENT, `UserId` varchar(128) NOT NULL, `ClaimType` longtext, `ClaimValue` longtext, PRIMARY KEY (`Id`), UNIQUE KEY `Id` (`Id`), KEY `UserId` (`UserId`), CONSTRAINT `AppUser_Claims` FOREIGN KEY (`UserId`) REFERENCES `user` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION) ENGINE=InnoDB DEFAULT CHARSET=utf8
$ UserLogin
CREATE TABLE `userlogin` ( `LoginProvider` varchar(128) NOT NULL, `ProviderKey` varchar(128) NOT NULL, `UserId` varchar(128) NOT NULL, PRIMARY KEY (`LoginProvider`,`ProviderKey`,`UserId`), KEY `AppUser_Logins` (`UserId`), CONSTRAINT `AppUser_Logins` FOREIGN KEY (`UserId`) REFERENCES `user` (`Id`) ON DELETE CASCADE ON UPDATE NO ACTION) ENGINE=InnoDB DEFAULT CHARSET=utf8
3. 如何使用ASP.NET Identity 3.1 認證(Authenticate)
using System.Security.Claims;using System.Web;using System.Web.Mvc;using Microsoft.AspNet.Identity;using Microsoft.AspNet.Identity.Owin;public class AccountController : Controller { [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public ActionResult Login(string name, string password, string returnUrl) { var userManager = HttpContext.GetOwinContext() .GetUserManager<UserManager<AppUser>>(); var authManager = HttpContext.GetOwinContext().Authentication; var user = userManager.Find(name, password); if (user == null) { // Invalid name or password } else { ClaimsIdentity identity = userManager.CreateIdentity( user, DefaultAuthenticationTypes.ApplicationCookie); authManager.SignOut(); authManager.SignIn(identity); return Redirect(returnUrl); } return View(); }}
3.2 使用者操作
using System.Security.Principal;using System.Web;using Microsoft.AspNet.Identity;using Microsoft.AspNet.Identity.Owin;var userManager = HttpContext.Current.GetOwinContext() .GetUserManager<UserManager<AppUser>>();// 擷取目前使用者IPrincipal principal = HttpContext.Current.User;AppUser user = userManager.FindByName(principal.Identity.Name);// 建立使用者var newUser = new AppUser { UserName = "Alice" };varr password = "changeme";userManager.Create(newUser, password);// 刪除使用者userManager.Delete(user);// 修改使用者資訊user.Email = "huangc126@126.com";user.PasswordHash = userManager.PasswordHasher.HashPassword("secret");
3.3 角色管理
using System.Web;using Microsoft.AspNet.Identity;using Microsoft.AspNet.Identity.EntityFramework;using Microsoft.AspNet.Identity.Owin;var roleManager = HttpContext.Current.GetOwinContext() .GetUserManager<RoleManager<IdentityRole>>();// 建立角色var newRole = new IdentityRole { Name = "Admin" };roleManager.Create(newRole);// 將角色授予使用者userManager.AddToRole(userId, role: "Admin");// 移除使用者的角色userManager.RemoveFromRole(userId, role: "Admin");// 刪除角色var role = roleManager.FindByName("Admin");roleManager.Delete(role);
3.4 授權(Authorization) 3.4.1 基於角色的授權
using System.Web.Mv;[Authorize(Roles = "Administrators")]public class AdminController : Controller { ...}
3.4.2 基於聲明(Claim)的授權
using System.Security.Claims;using System.Web;using System.Web.Mvc;[ClaimsAccess(Issuer = "RemoteClaims", ClaimType = ClaimTypes.PostalCode, Value = "123456")]public ActionResult Action() { ...}public class ClaimsAccessAttribute : AuthorizeAttribute { public string Issuer { get; set; }public string ClaimType { get; set; }public string Value { get; set; }protected override bool AuthorizeCore(HttpContextBase context) { return context.User.Identity.IsAuthenticated && context.User.Identity is ClaimsIdentity&& ((ClaimnsIdentity)context.User.Identity).HasClaim( c => c.Issuer == this.Issuer && c.Type == this.ClaimType&& c.Value == this.Value);}}
4. 小結
ASP.NET Identity非常靈活,支援各種擴充,對中小型系統來說足夠用了。雖然看上去有點麻煩,但即使是小系統,我也建議用Identity。因為自己去搞一套太麻煩,又容易出錯。我們應該把更多的精力花在業務實現上,而不是去摳底層技術細節。 大小: 14.7 KB 大小: 5.5 KB 大小: 4.9 KB 大小: 6.6 KB 大小: 5.6 KB 大小: 24.6 KB 查看圖片附件