Authentication与Authorization 1.Authentication对访问者的用户身份进行验证,“用户是否登陆成功”。 2.Authorization验证访问者用户身份是否又对资源访问的访问权限,“用户是否有权限访问这个地址”。
标识(Identity)框架 1.标识(Identity)框架:采用基于角色的访问控制(Role-Based Access Control,简称RBAC)策略,内置了对用户、角色等表的管理以及相关的接口,支持外部登录、2FA等。 2.标识框架使用EF Core对数据进行操作,因此标识框架支持几乎所有数据库。
Identity框架使用 1.IdentityUser<TKey>、IdentityRole<TKey>,TKey代表主键的类型。我们一般编写继承自IdentityUser<TKey>、IdentityRole<TKey>等的自定义类,可以增加自定义属性。 2.NuGet安装 Microsoft.AspNetCore.Identity.EntityFrameworkCore 3.创建继承自IdentityDbContext的类。 4.可以通过IdDbContext类来操作数据库,不过框架中提供了RoleManager、UserManager等类来简化对数据库的操作。 5.部分方法的返回值为Task<IdentityResult>类型,查看、讲解IdentityResult类型定义。
创建用户实体类User和角色实体类Role. 分别继承自IdentityUser<long>、IdentityRole<long>的User类和Role类。
1 2 3 4 5 public class User:IdentityUser<long> { public DateTime CreationTime { get; set; } public string? NickName { get; set; } }
1 2 3 public class Role :IdentityRole <long >{ }
IdnetityUser中定义类了很多属性。
除了IdentityUser和IdentityRole之外,表示框架中还有很多其他实体类。
创建继承自IdentityDbContext的类,这是一个EF Core中的上下文类,我们可以通过这个类操作数据库。IdentityDbContext是一个泛型类,有3个泛型参数,分别代表用户类型、
1 2 3 4 5 6 7 8 9 10 11 12 13 public class IdDbContext :IdentityDbContext <User ,Role ,long >{ public IdDbContext (DbContextOptions<IdDbContext> options ) : base (options ) { } protected override void OnModelCreating (ModelBuilder builder ) { base .OnModelCreating(builder); new UserConfig().Configure(builder.Entity<User>()); new RoleConfig().Configure(builder.Entity<Role>()); } }
向依赖注入容器中注册与标识框架相关的服务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 builder.Services.AddDbContext<IdDbContext>(opt => { string connStr = builder.Configuration.GetSection("Default" ).Value; opt.UseSqlServer(connStr); }); builder.Services.AddDataProtection(); builder.Services.AddIdentityCore<User>(opt => { opt.Password.RequireDigit = false ; opt.Password.RequireLowercase = false ; opt.Password.RequireNonAlphanumeric = false ; opt.Password.RequireUppercase = false ; opt.Password.RequiredLength = 6 ; opt.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider; opt.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider; }); var idBuilder = new IdentityBuilder(typeof (User), typeof (Role), builder.Services); idBuilder.AddEntityFrameworkStores<IdDbContext>() .AddDefaultTokenProviders().AddRoleManager<RoleManager<Role>>() .AddUserManager<UserManager<User>>();
编写控制器的代码。在控制器中需要对角色、用户进行操作,因此通过控制器的构造方法注入相关的服务。
1 2 3 4 5 6 7 8 9 10 11 public class DemoController : ControllerBase { private readonly RoleManager<Role> roleConfig; private readonly UserManager<User> userConfig; public DemoController (RoleManager<Role> roleConfig, UserManager<User> userConfig ) { this .roleConfig = roleConfig; this .userConfig = userConfig; } }
编写创建角色和用户的方法CreateUserRole.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 [HttpGet ] public async Task<ActionResult<string >> Test1(){ bool roleExists = await roleConfig.RoleExistsAsync("admin" ); if (!roleExists) { Role role = new Role { Name = "YOUXIANYU" }; var r = await roleConfig.CreateAsync(role); if (!r.Succeeded) { return BadRequest(r.Errors); } } User user = await this .userConfig.FindByNameAsync("admin" ); if (user == null ) { user = new User { UserName = "YOUXIANYU" , Email = "84289579@QQ.COM" , EmailConfirmed = true }; var r = await userConfig.CreateAsync(user,"123456" ); if (!r.Succeeded) { return BadRequest(r.Errors); } r = await userConfig.AddToRoleAsync(user, "YOUXIANYU" ); if (!r.Succeeded) { return BadRequest(r.Errors); } } return Ok("ok" ); }
检查登录用户信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 [HttpPost ] public async Task<ActionResult<string >> CheckPwd(CheckPwdRequest req){ string userName = req.userName; string password = req.password; var user = await userConfig.FindByNameAsync(userName); if (user == null ) return NotFound($"用户{userName} 不存在" ); if (await userConfig.IsLockedOutAsync(user)) return BadRequest("用户已锁定" ); var success = await userConfig.CheckPasswordAsync(user, password); if (success) { await userConfig.ResetAccessFailedCountAsync(user); return Ok("密码正确" ); } else { await userConfig.AccessFailedAsync(user); var count = await userConfig.GetAccessFailedCountAsync(user); if (count >= 5 ) { await userConfig.SetLockoutEndDateAsync(user, DateTimeOffset.UtcNow.AddMinutes(5 )); return BadRequest("用户已锁定" ); } return BadRequest($"密码错误,还有{5 - count} 次机会" ); } }
实现密码的重置 1.生成重置Token 2.Token发给客户(邮件、短信等),形式:连接、验证码等。 3.根据Token完成密码的重置。
编写一个发送重置密码请求的操作方法SendResetPasswordToken.
1 2 3 4 5 6 7 8 9 10 [HttpPost ] public async Task<ActionResult> SendResetPasswordToken (string userName ){ var user = await userConfig.FindByNameAsync(userName); if (user == null ) return NotFound($"用户{userName} 不存在" ); string token = await userConfig.GeneratePasswordResetTokenAsync(user); Console.WriteLine($"验证码是{token} " ); return Ok("ok" ); }
调用GeneratePasswordResetTokenAsyncc方法来生成一个密码重置令牌,这个令牌会保存到数据库中,然后我们把这个令牌发送到用户邮箱。
编写重置密码的操作方法VerifyResetPasswordToken。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 [HttpPut ] public async Task<ActionResult> ResetPassword (string userName,string token,string newPassword ){ var user = await userConfig.FindByNameAsync(userName); if (user == null ) return NotFound($"用户{userName} 不存在" ); var r = await userConfig.ResetPasswordAsync(user, token, newPassword); if (r.Succeeded) { await userConfig.ResetAccessFailedCountAsync(user); return Ok("重置密码成功" ); } else { await userConfig.AccessFailedAsync(user); return BadRequest("重置密码失败" ); } }