一、前言

上篇中简单介绍了 Asp.Net Core 自带的 Identity,一个负责对用户的身份进行认证的框架,当我们按需选择这个框架作为管理和存储我们应用中的用户账号数据的时候,就会添加到自己的项目当中去。这个时候,默认情况我们会使用自带的数据模型,但是考虑到需求的情况,我们可以通过自定义的方式更新数据模型,来达到我们的需求。

二、初识

上篇中我们完成了数据迁移加上项目的搭建,其中ApplicationDbContext是负责与Identity标识相关的用户,继承于IdentityDbContext上下文。当然了,我们发现根据这个上下文的扩展性,可以自定义用户数据,进行配置。

比如:自定义扩展的用户数据类来继承于IdentityUser类,更改用户数据模型属性,更改主键、更改表名列名等来满足我们的业务要求。

三、实践

接着上篇的WebIdentityDemoV3.1项目,将自定义用户数据添加到Identity DB,自定义扩展的用户数据类应继承IdentityUser类, 文件名为Areas / Identity / Data / {项目名称}User.cs。

3.1 表说明

这个就是我们要准备自定义的用户数据,本示例是直接继承于 Asp.Net Core 自带的 Identity的。

光从数据库表名上,我们就知道其中的含义了,就是用户角色管理。

数据说明:

_EFMigrationsHistory 是 Ef的迁移历史表。

AspNetUserClaimsAspNetRoleClaims是用户和角色的声明表,Claim在其中扮演者很重要的角色,甚至角色(Role)都被转换成了Claim,可以了解之前说到的认证授权模式。

AspNetUsersAspNetRolesAspNetUserRoles存储用户和角色信息。

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 中的模型自定义

Asp.Net Core之Identity应用(下篇)的更多相关文章

  1. ASP.NET Core 之 Identity 入门(三)

    前言 在上一篇文章中,我们学习了 CookieAuthentication 中间件,本篇的话主要看一下 Identity 本身. 最早2005年 ASP.NET 2.0 的时候开始, Web 应用程序 ...

  2. [转]ASP.NET Core 之 Identity 入门(三)

    本文转自:http://www.cnblogs.com/savorboard/p/aspnetcore-identity3.html 前言 在上一篇文章中,我们学习了 CookieAuthentica ...

  3. asp.net core使用identity+jwt保护你的webapi(一)——identity基础配置

    前言 用户模块几乎是每个系统必备的基础功能,如果每次开发一个新项目时都要做个用户模块,确实非常无聊.好在asp.net core给我们提供了Identity,使用起来也是比较方便,如果对用户这块需求不 ...

  4. asp.net core使用identity+jwt保护你的webapi(二)——获取jwt token

    前言 上一篇已经介绍了identity在web api中的基本配置,本篇来完成用户的注册,登录,获取jwt token. 开始 开始之前先配置一下jwt相关服务. 配置JWT 首先NuGet安装包: ...

  5. asp.net core使用identity+jwt保护你的webapi(三)——refresh token

    前言 上一篇已经介绍了identity的注册,登录,获取jwt token,本篇来完成refresh token. 开始 开始之前先说明一下为什么需要refresh token. 虽然jwt toke ...

  6. Asp.Net Core之Identity应用(上篇)

    一.前言 在前面的篇章介绍中,简单介绍了IdentityServer4持久化存储机制相关配置和操作数据,实现了数据迁移,但是未对用户实现持久化操作说明.在总结中我们也提到了, 因为IdentitySe ...

  7. ASP.NET Core 之 Identity 入门(一)

    前言 在 ASP.NET Core 中,仍然沿用了 ASP.NET里面的 Identity 组件库,负责对用户的身份进行认证,总体来说的话,没有MVC 5 里面那么复杂,因为在MVC 5里面引入了OW ...

  8. ASP.NET Core 之 Identity 入门(二)

    前言 在 上篇文章 中讲了关于 Identity 需要了解的单词以及相对应的几个知识点,并且知道了Identity处在整个登入流程中的位置,本篇主要是在 .NET 整个认证系统中比较重要的一个环节,就 ...

  9. [转]ASP.NET Core 之 Identity 入门(二)

    本文转自:http://www.cnblogs.com/savorboard/p/aspnetcore-identity2.html 前言 在 上篇文章 中讲了关于 Identity 需要了解的单词以 ...

随机推荐

  1. Android studio第一个程序HelloWorld

    今天主要跟着视频设计了第一个安卓项目,了解了大改的目录结构 每天会学习线性布局和相对布局

  2. LeetCode-079-单词搜索

    单词搜索 题目描述:给定一个 m x n 二维字符网格 board 和一个字符串单词 word .如果 word 存在于网格中,返回 true :否则,返回 false . 单词必须按照字母顺序,通过 ...

  3. TCP/IP方法安装打印机

    在计算机安装打印机驱动和添加打印机(TCP/IP方法) 一:打印机的型号和驱动. 1.1:如何知道自己的打印机是什么品牌和型号?: 通过观察打印机表面的logo或者其他文字图案来辨别 如下图: 1.2 ...

  4. Jmeter--由PV估算tps和最大并发数

    需求 "假设一个系统的业务有登录.浏览帖子.发送新贴.回复帖子,访问高峰是上午10点,日访问高峰PV约5208(含登录1300.浏览2706.发帖526.回帖676).系统响应时间要求小于3 ...

  5. linux中mariadb用navicat远程连接

    在Linux中创建数据库并且远程图形化工具连接 安装数据库 [root@node1 ~]# yum install mariadb-server -y #这里我使用的mariadb 其他数据库也可以 ...

  6. Linux下面怎么安装PHP扩展?

    一般开发环境都是在windows上, 安装扩展也很容易, 直接把下载的.dll文件扔到ext下面, 改一下php.ini文件. 完事了. linux上有两种安装方法 1.编译安装 //下载文件 #wg ...

  7. ASP.NET Core 6框架揭秘实例演示[23]:ASP.NET Core应用承载方式的变迁

    ASP.NET Core应用本质上就是一个由中间件构成的管道,承载系统将应用承载于一个托管进程中运行起来,其核心任务就是将这个管道构建起来.从设计模式的角度来讲,"管道"是构建者( ...

  8. LGP6146题解

    思维僵化了,习惯按照右端点排序,没想到是按照左端点排序... 考虑从左到右依次加入线段,考虑贡献. 设前 \(i\) 条线段的答案为 \(dp[i]\). 考虑两种情况: 不加,贡献为 \(dp[i- ...

  9. hashlib 模块 摘要算法

    应用于用户登陆,对密码进行加密操作, #文件操作 # hashlib 摘要算法 #md5 算法: 是32位的16进制组成的数字字符组成的字符串 #应用最广的摘要算法 #效率高,相对不复杂,如果只是传统 ...

  10. J20航模遥控器开源项目系列教程(四)PCB打印 | 嘉立创PCB下单教程,5元顺丰包邮解君愁!

    我们的开源宗旨:自由 协调 开放 合作 共享 拥抱开源,丰富国内开源生态,开展多人运动,欢迎加入我们哈~ 和一群志同道合的人,做自己所热爱的事! 项目开源地址:https://github.com/J ...