.NET Identity

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.EntityFramework

public 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 = "[email protected]";
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。因爲自己去搞一套太麻煩,又容易出錯。我們應該把更多的精力花在業務實現上,而不是去摳底層技術細節
 

發佈了3 篇原創文章 · 獲贊 6 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章