Asp.Net Core之Identity應用(下篇)

1、前言

上篇中簡單介紹了 Asp.Net Core 自帶的 Identity,一個負責對用戶的身份進行認證的框架,當咱們按需選擇這個框架做爲管理和存儲咱們應用中的用戶帳號數據的時候,就會添加到本身的項目當中去。這個時候,默認狀況咱們會使用自帶的數據模型,可是考慮到需求的狀況,咱們能夠經過自定義的方式更新數據模型,來達到咱們的需求。html

2、初識

上篇中咱們完成了數據遷移加上項目的搭建,其中ApplicationDbContext是負責與Identity標識相關的用戶,繼承於IdentityDbContext上下文。固然了,咱們發現根據這個上下文的擴展性,能夠自定義用戶數據,進行配置。git

好比:自定義擴展的用戶數據類來繼承於IdentityUser類,更改用戶數據模型屬性,更改主鍵、更改表名列名等來知足咱們的業務要求。github

3、實踐

接着上篇的WebIdentityDemoV3.1項目,將自定義用戶數據添加到Identity DB,自定義擴展的用戶數據類應繼承IdentityUser類, 文件名爲Areas / Identity / Data / {項目名稱}User.cs。shell

3.1 表說明

這個就是咱們要準備自定義的用戶數據,本示例是直接繼承於 Asp.Net Core 自帶的 Identity的。數據庫

光從數據庫表名上,咱們就知道其中的含義了,就是用戶角色管理。c#

數聽說明:框架

_EFMigrationsHistory 是 Ef的遷移歷史表。ide

AspNetUserClaimsAspNetRoleClaims是用戶和角色的聲明表,Claim在其中扮演者很重要的角色,甚至角色(Role)都被轉換成了Claim,能夠了解以前說到的認證受權模式。學習

AspNetUsersAspNetRolesAspNetUserRoles存儲用戶和角色信息。ui

AspNetUserTokens 用於外部驗證的Token存儲。

AspNetUserLogins 保留如 Google, Facebook, Twitter ,QQ等第三方登陸的信息。

3.2 自定義模型

上下文用於經過兩種方式配置模型:

  • 爲泛型類型參數提供實體和鍵類型。
  • 重寫 OnModelCreating 以修改這些類型的映射。

重寫時 OnModelCreatingbase.OnModelCreating 應首先調用,而後調用重寫配置。 EF Core 一般具備用於配置的最後一個 wins 策略。 例如,若是 ToTable 先使用一個表名稱調用實體類型的方法,而後再使用另外一個表名稱再次調用該方法,則使用第二個調用中的表名。

3.3 自定義數據

這裏以用戶類進行舉例說明:

3.3.1 自定義用戶類

定義ApplicationUser類繼承於IdentityUser用戶數據類, 自定義類命名約定 {Application}User。

public class ApplicationUser:IdentityUser
    { 
        /// <summary>
        /// 用戶編號
        /// </summary>
        public string UserNo { get; set; }
        /// <summary>
        /// 真實姓名
        /// </summary>
        public string UserTrueName { get; set; }

    }

3.3.2 修改服務配置

將原來Startup文件中的ConfigureServices服務配置中的IdentityUser改爲ApplicationUser

services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<ApplicationDbContext>();

改爲:

services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<ApplicationDbContext>();

3.3.3 修改上下文

將原來ApplicationDbContext上下文繼承於IdentityDbContext,改爲IdentityDbContext<ApplicationUser>

原來的

public class ApplicationDbContext : IdentityDbContext
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
    : base(options)
    {
    }
}

改爲:

ApplicationUser 類型用做上下文的泛型參數

public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options)
        {
        }
}

3.3.4 數據遷移

#1.存在數據庫則先刪除數據庫
#  Drop-Database (PMC) 或 dotnet ef database drop ( .NET Core CLI)
#2.確認刪除數據庫後,刪除遷移
#  Remove-Migration (PMC) 或 dotnet ef migrations remove ( .NET Core CLI)
# 再進行更新數據模型 ,添加遷移,轉換成相應的數據庫
PM> Add-Migration CreateIdentitySchema2 -c ApplicationDbContext -o Data/Migrations
PM> Update-Database CreateIdentitySchema2

效果以下:

能夠發現,有對應的自定義字段了。

3.3.5 更新替換

若是以前已經搭建好了項目,那麼你須要將IdentityUser類改爲自定義的ApplicationUser類。

更新 Pages/Shared/_LoginPartial ,並將替換 IdentityUserApplicationUser

@using Microsoft.AspNetCore.Identity
@using WebApp1.Areas.Identity.Data
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager

具體的其餘替換修改方法就不說明演示了。

3.4 更改主鍵類型

在建立數據庫以後更改PK列的數據類型在許多數據庫系統上都存在問題。更改PK一般涉及刪除和從新建立表。所以,在建立數據庫時,應在初始遷移中指定PK類型。下面是更改主鍵類型步驟:

3.4.1 更改表主鍵類型

這裏以ApplicationUser類爲例,修改相關代碼

// 用戶表設置主鍵爲Int
    public class ApplicationUser : IdentityUser<Guid>
    {
        /// <summary>
        /// 用戶編號
        /// </summary>
        public string UserNo { get; set; }
        /// <summary>
        /// 真實姓名
        /// </summary>
        public string UserTrueName { get; set; }
    }

3.4.2 修改上下文

    public class ApplicationDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>

3.4.3 修改服務配置

  services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
                .AddEntityFrameworkStores<ApplicationDbContext>();

3.4.4 數據遷移

#1.存在數據庫則先刪除數據庫
#  Drop-Database (PMC) 或 dotnet ef database drop ( .NET Core CLI)
#2.確認刪除數據庫後,刪除遷移
#  Remove-Migration (PMC) 或 dotnet ef migrations remove ( .NET Core CLI)
# 再進行更新數據模型 ,添加遷移,轉換成相應的數據庫
PM> Add-Migration CreateIdentitySchema2 -c ApplicationDbContext -o Data/Migrations
PM> Update-Database CreateIdentitySchema2 

此時表的主鍵類型已修改完成,包括關係表的外鍵類型也同步更新了,

效果以下:  

3.5 更改表名和列名

3.5.1 更改表名

再更改表名以前,請先調用 base.OnModelCreating 。而後,添加配置覆蓋默認表名,同時定義主鍵。這裏的示例以將默認表名改成以tbl開頭命名的表名

protected override void OnModelCreating(ModelBuilder builder)
        {
            var maxKeyLength = 256;
            base.OnModelCreating(builder);
            //自定義修改表名,以tbl命名開頭
            builder.Entity<ApplicationUser>(b =>
            {
                b.ToTable("TblUsers");
            });

            builder.Entity<IdentityUserClaim<Guid>>(b =>
            {
                //定義主鍵
                b.HasKey(u => u.Id);
                b.ToTable("TblUserClaims");
            });

            builder.Entity<IdentityUserLogin<Guid>>(b =>
            {
                b.HasKey(u => new { u.LoginProvider, u.ProviderKey });
                b.ToTable("TblUserLogins");
            });

            builder.Entity<IdentityUserToken<Guid>>(b =>
            {
                b.HasKey(u => new { u.UserId, u.LoginProvider, u.Name });
                b.ToTable("TblUserTokens");
            });

            builder.Entity<IdentityRole<Guid>>(b =>
            {
                b.HasKey(u => u.Id);
                b.ToTable("TblRoles");
            });

            builder.Entity<IdentityRoleClaim<Guid>>(b =>
            {
                b.HasKey(u => u.Id);
                b.ToTable("TblRoleClaims");
            });

            builder.Entity<IdentityUserRole<Guid>>(b =>
            {
                b.HasKey(u => new { u.UserId, u.RoleId });
                b.ToTable("TblUserRoles");
            });
        }
    }

若是使用之類的應用類型 ApplicationUser ,請配置該類型而不是默認類型。

3.5.2 更改列名

下面的示例將更改某些列名,按需更改

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<ApplicationUser>(b =>
    {
         b.Property(e => e.PasswordHash).HasColumnName("Password");
    });
}

3.5.3 更改長度

某些類型的數據庫列能夠配置某些 方面 (例如, string 容許) 最大長度。

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<ApplicationUser>(b =>
    {
        b.Property(u => u.UserName).HasMaxLength(128);
        b.Property(u => u.NormalizedUserName).HasMaxLength(128);
        b.Property(u => u.Email).HasMaxLength(128);
        b.Property(u => u.NormalizedEmail).HasMaxLength(128);
    }); 
}

3.5.4 數據遷移

#進行更新數據模型 ,添加遷移,轉換成相應的數據庫
PM> Add-Migration CreateIdentitySchema2 -c ApplicationDbContext -o Data/Migrations
PM> Update-Database CreateIdentitySchema2 

此時表的主鍵類型已修改完成,包括關係表的外鍵類型也同步更新了,

效果以下:  

3.6 初始化數據庫

在建立項目時候,咱們能夠提早作好初始化數據的準備,將數據做爲種子處理遷移到建立的數據庫中進行初始化操做。

3.6.1 建立文件

建立SeedData.cs文件,用於初始化基礎數據:

public class SeedData
    {
        public static void EnsureSeedData(IServiceProvider serviceProvider)
        {
            Console.WriteLine("Seeding database...");
            using (var scope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
            {
                var context = scope.ServiceProvider.GetService<ApplicationDbContext>();
                context.Database.Migrate();

                var userMgr = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();
                var alice = userMgr.FindByNameAsync("alice").Result;
                if (alice == null)
                {
                    alice = new ApplicationUser
                    {
                        UserName = "alice"
                    };
                    var result = userMgr.CreateAsync(alice, "Pass123$").Result;
                    if (!result.Succeeded)
                    {
                        throw new Exception(result.Errors.First().Description);
                    }

                    result = userMgr.AddClaimsAsync(alice, new Claim[]{
                        new Claim(JwtClaimTypes.Name, "Alice Smith"),
                        new Claim(JwtClaimTypes.GivenName, "Alice"),
                        new Claim(JwtClaimTypes.FamilyName, "Smith"),
                        new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"),
                        new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                        new Claim(JwtClaimTypes.WebSite, "http://alice.com")
                    }).Result;
                    if (!result.Succeeded)
                    {
                        throw new Exception(result.Errors.First().Description);
                    }
                    Console.WriteLine("alice created");
                }
                else
                {
                    Console.WriteLine("alice already exists");
                }

                var bob = userMgr.FindByNameAsync("bob").Result;
                if (bob == null)
                {
                    bob = new ApplicationUser
                    {
                        UserName = "bob"
                    };
                    var result = userMgr.CreateAsync(bob, "Pass123$").Result;
                    if (!result.Succeeded)
                    {
                        throw new Exception(result.Errors.First().Description);
                    } 
                    result = userMgr.AddClaimsAsync(bob, new Claim[]{
                        new Claim(JwtClaimTypes.Name, "Bob Smith"),
                        new Claim(JwtClaimTypes.GivenName, "Bob"),
                        new Claim(JwtClaimTypes.FamilyName, "Smith"),
                        new Claim(JwtClaimTypes.Email, "BobSmith@email.com"),
                        new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                        new Claim(JwtClaimTypes.WebSite, "http://bob.com"),
                        new Claim("location", "somewhere")
                    }).Result;
                    if (!result.Succeeded)
                    {
                        throw new Exception(result.Errors.First().Description);
                    }
                    Console.WriteLine("bob created");
                }
                else
                {
                    Console.WriteLine("bob already exists");
                }
            }

            Console.WriteLine("Done seeding database.");
            Console.WriteLine();
        }
    }

配置添加自定義用戶信息和身份。

3.6.2 調用方法

而後咱們能夠從主入口Main方法調用它:

public static void Main(string[] args)
        {
            var seed = args.Contains("/seed");
            if (seed)
            {
                args = args.Except(new[] { "/seed" }).ToArray();
            }
            var host = CreateHostBuilder(args).Build();
            if (seed)
            {
                SeedData.EnsureSeedData(host.Services);
            }
            host.Run();
        }

3.6.3 程序運行

輸入 dotnet run /seed 

3.6.4 效果

總結

  1. 本篇簡單介紹了對Identity自定義用戶以及表結構說明,以及根據自定義更改生成模型,並添加到示例項目當中。
  2. 後續會將此身份認證機制來應用到IdentityServer4中使用,進行用戶角色管理存儲操做。
  3. 若是有不對的或不理解的地方,但願你們能夠多多指正,提出問題,一塊兒討論,不斷學習,共同進步。
  4. 項目地址

附加

Identity ASP.NET Core 中的模型自定義

相關文章
相關標籤/搜索