8.1标识框架

8.1标识框架

ASP.NET Core提供了标识框架,采用RBAC(基于角色的访问控制),内置了对用户、角色等表的管理及相关接口,框架中提供了IdentityUser<TKey>IdentityRole<TTKey>两个实体类型,Tkey为主键类型。

使用步骤:
1. NuGet安装Microsoft.AspNetCore.Identity.EntityFrameworkCore
2. 创建用户实体类和角色实体类
//每次直接使用IdentityUser<long>都要说明主键类型,所以直接继承
//IdentityUser类中已经内置了用户名、密码、邮箱等属性,如果想增加自己的属性,也可以使用继承
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
//泛型参数分别代表用户类型、角色类型、主键类型
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);
	}
}

可以通过这个类操作数据库,但是标识框架提供了RoleManagerUserManager类简化对数据库的操作,这些类封装了对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. 向容器中注册服务
IServiceCollection services = builder.Services;
//对IdDbContext进行设置
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-MigrationUpdate-database生成数据库
6. 编写控制器代码,对角色、用户进行操作
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");//判断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)
		{
            //EmailConfirmed设置为true
            //使用邮箱注册时,发送验证码到邮箱,用户输入验证码后才能确认这个邮箱可用,EmailConfirmed属性表示邮箱是否确认过
            //如果邮箱确认是存在的,则可以像下面 这样直接使用
            //如果创建用户的时候不确定邮箱是否可用,则需要先调用UserManager的GenerateEmailConfirmationTokenAsync创建
            //一个字符串作为“确认令牌”,服务器将确认令牌发送到用户邮箱,用户在输入确认令牌的时候,调用UserManager的
            //ConfirmEmailAsync方法来验证令牌
			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. 编写登陆请求的操作方法
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
	{
        //密码错误则记录一次登陆失败,达到次数后就锁定账户一段时间,防止暴力破解
        //失败次数和锁定时间可以在AddIdentityCore中设定
        //option.Lockout.DefaultLockoutTimesSpan和option.Lockout.MaxFailedAccessAttempts来修改
		var r = await userManager.AccessFailedAsync(user);
		if (!r.Succeeded)
		{
			return BadRequest("AccessFailed failed");
		}
		return BadRequest("Failed");
	}
}

实现密码重置

发送重置密码的请求

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();
}

重置密码

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();
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

步、步、为营

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值