Asp.net core 身份认证框架 Microsoft Identity的使用以及如何使用Idengtity创建自带的用户模型SignInManager和UserManager的使用等等

本文详细介绍了如何在ASP.NET Core中实现身份认证组件,包括更改数据库上下文,安装并配置Identity框架,自定义用户模型,以及使用UserManager和SignInManager进行用户注册和登录操作。此外,还涉及到数据迁移、JWT令牌的生成以及种子数据的初始化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

要启动身份认证组件,首先要更改数据库上下文中继承的关系,原本我们是继承于在这里插入图片描述
Dbcontext这个类,现在我们需要继承IdentityDbContext
继承这个类,我们需要安装对应的框架,
如图:
在这里插入图片描述
这里注意,版本一定要和你.net core 框架版本一致,不然不匹配。
框架安装完成后,我们进入数据库上下文类引入框架。
在这里插入图片描述
接着,将DbContext替换为IdentityDbContext
在这里插入图片描述
其中IdentityDbContext里的泛型IdentityUser就是身份认证的数据库结构,相当于UserModel也就是用户模型,里面有自定义好的相关用户信息字段。比如ID,姓名,性别什么的。这些定义都是由.net core 自动完成的,也会映射到数据库中。有了数据库结构后,IdentityDbContext会自动为我们的系统添加与用户表相对应的映射关系,如果数据库的用户表,UserTable还不存在,IdentityDbContext可以帮我们更新数据库,自动为我们添加用户表,这样通过数据库上下文对象,我们系统就可以自动获取到用户的信息了。而我们不需要为实现用户模块写一丁点代码。

对于IdentityUser如果默认结构不能满足你的需求,你也可以通过继承IdentityUser去修改用户模型。
补充点:记得初始化构造函数

 public AppDBcontext(DbContextOptions<AppDBcontext> options) : base(options)
        {
        }

好了,接着
在创建数据库之前,我们需要给框架注册服务认证的依赖。
我们找到Startup.cs文件
在ConfigureServices这个函数中添加函数依赖,代码如下:

  ///注册身份认证的服务依赖  
            ///泛型俩个参数分别是用户数据模型和角色模型
            ///AddEntityFrameworkStores 通过这个函数连接数据库上下文对象   泛型为对应的上下文对象。
            services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<AppDbContext>();

框架注入成功后,我们来进行数据更新。
这里使用.net 的命令进行数据迁移,不会数据迁移的请看这篇文章
在项目根目录下 进行CMD
输入一下命令 进行数据迁移
在这里插入图片描述
执行完成后我们可以在项目中看到对应的项目迁移代码。
在这里插入图片描述
我们可以在代码中的UP函数中看到,代码自动编写了大概创建了八张表。可能很多人有疑问了,我明明没有添加这些表,为什么系统给我加了这么多乱七八糟的表呢,
我们打开数据库上下文,其实就是因为你继承了IdentityDbContext所以也就继承了IdentityDbContext自带的数据模型,以及数据模型所对应的数据库表,接下来让我回到命令行,执行更新数据库。
在这里插入图片描述
输入红线代码进行数据库更新。
执行完成后我们可以去数据库看到更新的表
在这里插入图片描述
这些表就是我们用户模块的新表。现在项目的用户数据就创建完成了。

接着我们试着用这写数据库,做一个注册功能。

首先创建一个接收注册消息的Dto

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
namespace Fakexiecheng.API.Dtos
{
    public class RegisterDto
    {
        [Required]//Required注释  为必填字段
        public string Email { get; set; }
        [Required]
        public string Password { get; set; }
        [Required]
        [Compare(nameof(Password),ErrorMessage ="输入的密码不一致")]
        public string CofirmPassword { get; set; }
    }
}

接着我们在对应的Api里写好对应的注册函数

   private readonly UserManager<IdentityUser> _userManager;//可以通过这个工具对密码进行加密  泛型为定义的用户模型
 /// <summary>
        /// 用户注册
        /// </summary>
        /// <param name="registerDto">注册信息</param>
        /// <returns></returns>
        [AllowAnonymous]
        [HttpPost("register")]
        public async Task<IActionResult> Register([FromBody] RegisterDto registerDto)
        {
            //1 使用用户名创建用户对象
            var user = new IdentityUser()
            {
                UserName = registerDto.Email,
                Email = registerDto.Email
            };
            //2 hash密码,保存用户
            var result = await _userManager.CreateAsync(user, registerDto.Password);//hash密码并连同用户模型对象 一起保存打数据库中
            //如果成功表示  用户创建成功并且保存起来了

            //不成功
            if (!result.Succeeded) {
                //返回400
                return BadRequest();
            }
            //3 return
            //成功  200
            return Ok();
        
        }

其中hash密码的时候,我们用的Identity自带的工具 UserManager。代码中都有注释 自己看吧,
接着我们来试着用Postman进行请求一下。
在这里插入图片描述
可以看到没什么问题,而且UserManager可以帮你自动把用户名是否相同的验证都做了,就是CreateAsync用它哈希加密后 貌似不能解密。可能我没找到方法吧,而且UserManager对密码做的有限制,看我上面图密码搞那么复杂 你应该就懂了,太简单,是无法注册的。。。内部是如何验证的我也不知道,,,这个还等着网友们去探秘。可以下载微软自己提供的反编译工具,叫什么来着 ,忘记了,自己可以看看CreateAsync这个方法的内部结构,相信你可以找到答案,,,

接着把数据库接入登录系统中

首先用Identity框架下的SignInManager来处理用户登录验证,同时使用UserManager获取用户信息,并且提取用户权限,并转换为Claim使用JWT输出。
首先我们在控制器中注入服务依赖。
在这里插入图片描述
添加划线的代码,.net core 服务注入都是通过构造函数实现的,这个我想大家都知道吧。

接下来我们使用SignInManager来进行用户验证,
登录方法中加入一下代码:

        [AllowAnonymous]//允许所有人访问
        [HttpPost("login")]
        public async  Task<IActionResult> login([FromBody] LoginDto loginDto) 
        {
            //1.进行信息认证
            var loginResult = await _signInManager.PasswordSignInAsync(loginDto.Email,loginDto.password
                , false  //指示在关闭浏览器后登录 Cookie 是否应该保留的标志。
                , false//多次登录失败后 是否锁定账号
                );
            //判断是否验证成功
            if (!loginResult.Succeeded) 
            {
                // 400
                return BadRequest();
            }
            //获取用户信息
            var user = await _userManager.FindByNameAsync(loginDto.Email);

           

            //2.创建JWT  Token
            //header   singningAlgorithm储存编码算法
            var singningAlgorithm = SecurityAlgorithms.HmacSha256;
            //payload   需要用到的数据
            var claims = new List<Claim> { 
           // sub ==jwt的ID    
           //等同于  Sub:fake_user_id
           new Claim(JwtRegisteredClaimNames.Sub,user.Id),
          // new Claim(ClaimTypes.Role,"Admin")//角色信息

           };
            //获取用户角色
            var roleNames = await _userManager.GetRolesAsync(user);
            //遍历角色  一个用户可能有多个角色
            foreach (var roleName in roleNames)
            {
                var roleClaim = new Claim(ClaimTypes.Role, roleName);
                claims.Add(roleClaim);
            }

            //signature   数字签名   需要用到私钥
            //私钥一般放在配置文件中  私钥是自定义的  想写什么写什么

            //使用utf进行编码
            var secretByte = Encoding.UTF8.GetBytes(_configuration["Authentication:SecretKey"]);
            //使用非对称算法 对私钥加密
            var signingkey = new SymmetricSecurityKey(secretByte);
            //通过256验证非对称加密的私钥
            var signingCredentials = new SigningCredentials(signingkey, singningAlgorithm);

            //创建token
            var token = new JwtSecurityToken(
                issuer:_configuration  ["Authentication:Issuer"],//谁发布的TOken
                audience:_configuration["Authentication:Audience"],//token发布给谁
           claims,//payload数据
           notBefore:DateTime.UtcNow,//发布时间
           expires:DateTime.UtcNow.AddDays(1),//有效时间
           signingCredentials//数字签名
                );

            //以字符串形式 输出Token
            var tokenStr = new JwtSecurityTokenHandler().WriteToken(token);

            //3.返回jwt字符串

            return Ok(tokenStr);
        }

接下来我们来postman测试一下。
首先注册用户
在这里插入图片描述

然后用注册的用户登录系统
在这里插入图片描述

然后通过返回的JWT来进行一下数据访问
在这里插入图片描述

我们发现404
其实这里有个天大的坑。
原因:在我们使用Identity框架的多角色验证时,验证中间件使用的并不是JWT验证,和默认验证并不匹配。
解决办法
显示指定使用JWT 的验证方式。
再要访问的所有需要权限认证方法头部加上一下代码:

   [Authorize(AuthenticationSchemes = "Bearer")]

接着测试一下
在这里插入图片描述
这下可以看到 返回403权限不足而不是404了 这是因为我规定了只有管理员才可以创建东西。所以这是正确的。

接着我们来定制一下用户模型

场景:当我们用的默认用户模型字段不够的时候,我么可以通过继承来定制一个用户模型。比如下图中
在这里插入图片描述
自带的这些字段不够用呢,就可以通过定制来扩展、

我们来看看数据库结构


表示用户模型
在这里插入图片描述
用于保存直接与用户权限绑定的声明,比如只允许张三可以删除数据。
在这里插入图片描述
用于第三方登录工具,比如记录微信登录的信息。
在这里插入图片描述
用于保存用户角色信息,一个用户可能有多个角色
在这里插入图片描述
记录用户登录的session,比如登录时长,拉黑用户都在这个表记录。

接着我们从代码的角度重载一下这几张表,首先新建一个ApplicationUser类继承IdentityUser,然后添加一下代码,这里切记 代码一定要一致,因为名称要和父类名称一致,不然会失败。

using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Fakexiecheng.API.Models
{
    public class ApplicationUser:IdentityUser
    {
        public string Address { get; set; }
        //shoppingCart
        //orders
        public virtual ICollection<IdentityUserRole<string>> UserRoles { get; set; }

        public virtual ICollection<IdentityUserClaim<string>> Claims { get; set; }

        public virtual ICollection<IdentityUserLogin<string>> Logins { get; set; }

        public virtual ICollection<IdentityUserToken<string>> Tokens { get; set; }


    }
}

然后我们把用到的IdentityUser全部替换为ApplicationUser

接着我们可以注入一下种子数据进行数据的更新、
接着我进入上下文关系文档,在OnModelCreating函数中进行种子数据的初始化。
添加如下代码:

 //初始化用户与角色的种子数据
            //1.更新用户与角色的外键
            //HasMany 表示一对多的关系  这个关系可以被映射为roles 每一个role都有一个外键关系 WithOne  而这个外键关系使用的是UserID  这个ID是必须的  所以加上IsRequired()
            modelBuilder.Entity<ApplicationUser>(u => u.HasMany(x => x.UserRoles).WithOne().HasForeignKey(ur => ur.UserId).IsRequired());

            //2.添加管理员角色

            //给角色添加主键
            var adminRoleId = Guid.NewGuid().ToString();
            modelBuilder.Entity<IdentityRole>().HasData(
                new IdentityRole()
                {
                    Id = adminRoleId,
                    Name = "Admin",
                    NormalizedName = "Admin".ToUpper()//大写
                }
                ); 
            //3.添加用户
            var adminUserID = Guid.NewGuid().ToString();
            ApplicationUser adminUser = new ApplicationUser
            {
                Id = adminUserID,
                UserName = "13011@qq.com",
                NormalizedUserName = "13011@qq.com".ToUpper(),
                Email= "13011@qq.com",
                NormalizedEmail= "13011@qq.com".ToUpper(),
                TwoFactorEnabled=false,
                EmailConfirmed=true,
                PhoneNumber="1234567891",
                PhoneNumberConfirmed=false


            };
            //hash密码
            var ph = new PasswordHasher<ApplicationUser>();
            adminUser.PasswordHash = ph.HashPassword(adminUser, "Fake123$");
            modelBuilder.Entity<ApplicationUser>().HasData(adminUser);

            //4.给用户加入管理员角色
            modelBuilder.Entity<IdentityUserRole<string>>().HasData(new  IdentityUserRole<string>(){ 

            RoleId=adminRoleId,
            UserId=adminUserID
            
            });

然后我们进行数据迁移。
在这里插入图片描述
然后我们去数据迁移文件里解读一下代码
在这里插入图片描述
我们可以看到 ,添加了对应的数据和字段。
接着我们去用一下我们的种子数据。
在这里插入图片描述

在这里插入图片描述

我们发现可以成功登录并且创建数据。好了 就更新这么多了。。。。写的有点乱,希望不要介意。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值