标识框架(Identity) Authentication与Authorization 1、Authentication对访问者的用户进行验证,“用户是否登录”。 2、Authorization验证访问者的用户身份是否有对资源访问的访问权限,“用户是否有权限访问地址” 用户可能有多个角色,一个角色可能有多个用户。
标识框架概念 1、Identity采用基于角色的访问控制(RBAC)策略,内置了对用户、角色等表的管理,以及相关的接口,支持外部登录、2FA(两阶段验证)等。 2、标识框架使用EFCore对数据库进行操作,因此标识框架几乎支持所有数据库。
简单使用 1、创建用户类和角色类,一般不自己编写而是创建继承IdentityUser<TKey>、IdentityRole<TKey>的自定义类类,TKey代表主键的类型,可以增加自定义属性。 2、NuGet安装: Microsoft.AspNetCore.Identity.EntityFrameworkCore。 3、创建继承自IdentityDbContext的类,这个类内置了对IdentityUser<TKey>、IdentityRole<TKey>的处理,同时提供了基本的增删改查的操作。 4、可以通过IdentityDbContext类来操作数据库,不过框架中提供了RoleManager、UserManager等类来简化对数据库的操作。 5、部分方法的返回值为Task<IdentityResult>类型,这个类型包含着操作是否成功与错误信息等说明,需要手动判断 记得执行数据库迁移命令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 //自定义Identity角色 public class MyRole : IdentityRole<long>{ } //自定义Identity用户 public class MyUser: IdentityUser<long> { public string? WeiXinAccount { get; set; } } //数据库连接 //不要忘了<MyUser,MyRole,long> public class MyDbContext :IdentityDbContext<MyUser,MyRole,long>{ public MyDbContext(DbContextOptions<MyDbContext> options) : base(options) { } } //数据库迁移时(Add-Migration Init报错)如果报错就定义这个类 public class DbContextDesignTimeFactory : IDesignTimeDbContextFactory<MyDbContext> { public MyDbContext CreateDbContext(string[] args) { DbContextOptionsBuilder<MyDbContext> builder = new DbContextOptionsBuilder<MyDbContext>(); var serverVersion = new MySqlServerVersion(new Version(8, 0, 29)); builder.UseMySql("server=localhost;user=root;password=your_password;database=your_database", serverVersion); return new MyDbContext(builder.Options); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 //Program.cs //Identity builder.Services.AddDbContext<MyDbContext>(opt => { string constr = "server=localhost;user=root;password=your_password;database=your_database"; var serverVersion = new MySqlServerVersion(new Version(8, 0, 29)); opt.UseMySql(constr, serverVersion); }); builder.Services.AddDataProtection(); builder.Services.AddIdentityCore<MyUser>(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;//邮箱配置 }); IdentityBuilder idBuilder = new IdentityBuilder(typeof(MyUser), typeof(MyRole), builder.Services); idBuilder.AddEntityFrameworkStores<MyDbContext>() .AddDefaultTokenProviders() .AddUserManager<UserManager<MyUser>>() .AddRoleManager<RoleManager<MyRole>>();
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 50 51 52 53 54 55 56 57 58 59 //Controller [Route("[controller]/[action]")] [ApiController] public class DemoController : ControllerBase { private readonly UserManager<MyUser> userManager; private readonly RoleManager<MyRole> roleManager; private IWebHostEnvironment env; public DemoController(UserManager<MyUser> userManager, RoleManager<MyRole> roleManager, IWebHostEnvironment env) { this.userManager = userManager; this.roleManager = roleManager; this.env = env; } [HttpPost] //创建用户 public async Task<ActionResult<string>> Test1() { if(await roleManager.RoleExistsAsync("admin")==false) { MyRole role = new MyRole() { Name = "admin"}; var result = await roleManager.CreateAsync(role); if (!result.Succeeded) return BadRequest("roleManager,CreateAsync failed"); } MyUser user1 =await userManager.FindByNameAsync("LJH"); if(user1==null) { user1 = new MyUser { UserName = "LJH" }; var result = await userManager.CreateAsync(user1, "123456"); if (!result.Succeeded) return BadRequest("userManager,CreateAsync failed"); } if(! await userManager.IsInRoleAsync(user1, "admin")) { var result = await userManager.AddToRoleAsync(user1, "admin"); if (!result.Succeeded) return BadRequest("userManager,AddToRoleAsync failed"); } return "ok"; } [HttpPost] public async Task<ActionResult> CheckPwd(CheckPwdRequest req) { string userName = req.UserName; string pwd = req.Password; var user = await userManager.FindByNameAsync(userName); if(user==null) { if(env.IsDevelopment()) { return BadRequest("用户名不存在"); } else { return BadRequest();//更安全 } } if(await userManager.IsLockedOutAsync(user)) { return BadRequest("用户已被锁定,锁定结束时间"+user.LockoutEnd); } if(await userManager.CheckPasswordAsync(user, pwd)) { await userManager.ResetAccessFailedCountAsync(user); return Ok("登录成功"); } else { await userManager.AccessFailedAsync(user); return BadRequest("用户名或密码错误"); } } }
密码重置 流程: 1、生成重置Token 2、Token发给客户,形式:连接、验证码 3、根据Token实现密码重置
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 [HttpPost] public async Task<ActionResult> SendRestPasswordToken(string userName) { var user = await userManager.FindByNameAsync(userName); if(user==null) { return BadRequest("用户名不存在"); } string token = await userManager.GeneratePasswordResetTokenAsync(user); Console.WriteLine("验证码是" + token); return Ok(); } [HttpPut] public async Task<ActionResult> ResetPassword(string userName,string token,string newPassword) { var user = await userManager.FindByNameAsync(userName); if (user == null) { return BadRequest("用户名不存在"); } var result = await userManager.ResetPasswordAsync(user, token, newPassword); if(result.Succeeded) { await userManager.ResetAccessFailedCountAsync(user); return Ok("密码重置成功"); } else { await userManager.ResetAccessFailedCountAsync(user); return BadRequest("密码重置失败"); } }