8.1标识框架
ASP.NET Core提供了标识框架,采用RBAC(基于角色的访问控制),内置了对用户、角色等表的管理及相关接口,框架中提供了IdentityUser<TKey>
和IdentityRole<TTKey>
两个实体类型,Tkey为主键类型。
使用步骤:
1. NuGet安装Microsoft.AspNetCore.Identity.EntityFrameworkCore
2. 创建用户实体类和角色实体类
1 2 3 4 5 6 7 8 9 10 11
|
public class User : IdentityUser<long> { public DateTime CreationTime { get; set; } public string? NickName { get; set; } }
public class Role : IdentityRole<long> { }
|
除了IdentityUser和IdentityRole,标识框架中还有IdentityRoleClaim、IdentityUserToken等实体类,这些实体类都有默认的表名,如果要修改,可以使用IEntityTypeConfiguration来对实体类进行配置
3. 创建标识上下文类,继承自IdentityDbContext
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 modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly); } }
|
可以通过这个类操作数据库,但是标识框架提供了RoleManager
、UserManager
类简化对数据库的操作,这些类封装了对IdentityDbContext的操作。
标识框架中的方法有执行失败的可能,所以有些方法可以通过Task<IdentityResult>
的返回结果来验证是否失败,IdentityResult的Succeeded
属性表示是否操作成功,如果失败,则可以从Errors
属性中获取错误信息,
RoleManager常用方法:
方法 | 说明 |
---|
Task<IdentityResult> CreateAsync(TRole role) | 创建角色 |
Task<IdentityResult> DeleteAsync(TRole role) | 删除角色 |
Task<bool> RoleExistsAsync(string roleName) | 指定名字的角色是否存在 |
Task<TRole> FindByNameAsync(string roleName) | 根据角色名字获取角色对象 |
UserManager常用方法:
方法 | 说明 |
---|
Task<IdentityResult> CreateAsync(TUser user,string password) | 创建用户 |
Task<IdentityResult> UpdateAsync(TUser user) | 更新用户 |
Task<IdentityResult> DeleteAsync(TUser user) | 删除用户 |
Task<IUser> FindByIdAsync(string userId) | 根据Id查找用户 |
Task<IUser> FindByNameAsync(string userName) | 根据name查找用户 |
Task<Bool> CheckPasswordAsync(TUser user,string password) | 检查用户密码是否正确,如果失败则调用AccessFailedAsync记录失败次数 |
Task<IdentityResult> ChangePasswordAsync(TUser user,string currentPassword,string newPassword) | 修改密码 |
Task<string> GeneratePasswordResetTokenAsync(TUser user) | 生成令牌,用来重置密码 |
Task<IdentityResult> ResetPasswordAsync(TUser user,string token,string newPassword) | 重置密码 |
Task<IdentityResult> AddToRoleAsync(TUser user,string role) | 为用户增加角色 |
Task<IdentityResult> RemoveFromRoleAsync(TUser user,string role) | 为用户删除角色 |
Task<IList<string>> GetRolesAsync(TUser user) | 用户所拥有的所有角色 |
Task<bool> IsInRoleAsync(TUser user,string role) | 判断用户是否具有某个角色 |
Task<bool> IsLockedOutAsync(TUser user) | 判断用户是否被锁定 |
Task<DataTimeOffset?> GetLockoutEndDataAsync(TUser user) | 获取锁定时间 |
Task<DataTimeOffset> SetLockoutEndDataAsync(TUser user,DataTimeOffset? lockoutEnd) | 设置用户锁定时间 |
Task<IdentityResult> AccessFailedAsync(TUser user) | 记录用户登陆失败次数,多次失败应当锁定一段时间 |
4. 向容器中注册服务
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
| IServiceCollection services = builder.Services;
services.AddDbContext<IdDbContext>(opt => { string connStr = builder.Configuration.GetConnectionString("Default"); opt.UseSqlServer(connStr); }); services.AddDataProtection();
services.AddIdentityCore<User>(options => { options.Password.RequireDigit = false; options.Password.RequireLowercase = false; options.Password.RequireNonAlphanumeric = false; options.Password.RequireUppercase = false; options.Password.RequiredLength = 6; options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider; options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider; }); var idBuilder = new IdentityBuilder(typeof(User), typeof(Role), services);
idBuilder.AddEntityFrameworkStores<IdDbContext>() .AddDefaultTokenProviders() .AddRoleManager<RoleManager<Role>>() .AddUserManager<UserManager<User>>();
|
5. 使用Add-Migration
,Update-database
生成数据库
6. 编写控制器代码,对角色、用户进行操作
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public class Test1Controller : ControllerBase { private readonly ILogger<Test1Controller> logger; private readonly RoleManager<Role> roleManager; private readonly UserManager<User> userManager; public Test1Controller(ILogger<Test1Controller> logger, RoleManager<Role> roleManager, UserManager<User> userManager) { this.logger = logger; this.roleManager = roleManager; this.userManager = userManager; } [HttpPost] public async Task<ActionResult> CreateUserRole() { bool roleExists = await roleManager.RoleExistsAsync("admin"); if (!roleExists) { Role role = new Role { Name="Admin"}; var r = await roleManager.CreateAsync(role); if (!r.Succeeded) { return BadRequest(r.Errors); } } User user = await this.userManager.FindByNameAsync("yzk"); if (user == null) { user = new User{UserName="yzk",Email="yangzhongke8@gmail.com",EmailConfirmed=true}; var r = await userManager.CreateAsync(user, "123456"); if (!r.Succeeded) { return BadRequest(r.Errors); } r = await userManager.AddToRoleAsync(user, "admin"); if (!r.Succeeded) { return BadRequest(r.Errors); } } return Ok(); } }
|
7. 编写登陆请求的操作方法
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 31 32 33 34
| public record LoginRequest(string UserName,string Password);
[HttpPost] public async Task<ActionResult> Login(LoginRequest req) { string userName = req.UserName; string password = req.Password; var user = await userManager.FindByNameAsync(userName); if (user == null) { return NotFound($"用户名不存在{userName}"); } if (await userManager.IsLockedOutAsync(user)) { return BadRequest("LockedOut"); } var success = await userManager.CheckPasswordAsync(user, password); if (success) { return Ok("Success"); } else { var r = await userManager.AccessFailedAsync(user); if (!r.Succeeded) { return BadRequest("AccessFailed failed"); } return BadRequest("Failed"); } }
|
实现密码重置
发送重置密码的请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public record SendResetPasswordTokenRequest(string Email);
[HttpPost] public async Task<IActionResult> SendResetPasswordToken( SendResetPasswordTokenRequest req) { string email = req.Email; var user = await userManager.FindByEmailAsync(email); if (user == null) { return NotFound($"邮箱不存在{email}"); } string token = await userManager.GeneratePasswordResetTokenAsync(user); logger.LogInformation($"向邮箱{user.Email}发送Token={token}"); return Ok(); }
|
重置密码
1 2 3 4 5 6 7 8 9 10 11 12 13
| public record VerifyResetPasswordRequest(string Email,string token,string newPassword);
public async Task<IActionResult> VerifyResetPassword( SendResetPasswordTokenRequest req) { string email = req.Email; var user = await userManager.FindByEmail(email); string token = req.Token; string password = req.NewPassword; var r = await userManager.ResetPasswordAsync(user,token,password); return Ok(); }
|