Identity标识框架

你会弹吉他吗?不会?那你为什么能拨动我的心弦。 --zhu
Authentication与Authorization
1、Authentication对访问者的用户身份进行验证,“用户是否登录成功”。
2、Authorization验证访问者的用户身份是否有对资源访问的访问权限,“用户是否有权限访问这个地址”。

标识(Identity)框架

ASP.NET Core 内置的标识(identity)框架,采用的是 RBAC(role-based access control,基于角色的访问控制)策略,是一个用于管理用户身份验证、授权和安全性的框架。

它提供了一套工具和库,用于管理用户、角色、登录、密码重置、电子邮件确认等功能。

通过它,你可以:

1、用户管理:创建、管理和验证用户,这样你可以轻松操作注册用户、登录、注销、密码重置等功能。

2、角色管理:定义不同的用户角色,并将用户分配到这些角色中,这样你可以更好地控制用户的权限和访问级别。

3、密码策略:框架提供了密码策略功能,允许你定义密码的复杂度要求,例如密码长度、大小写字母、数字等要求。

4、外部登录:框架还支持你使用外部身份验证提供者(如 QQ、微信、微博等)进行身份验证。

5、电子邮件确认:通过电子邮件确认用户注册和密码重置操作,这样注册和修改密码操作更加安全可靠。

Identity框架使用
1、IdentityUser、IdentityRole,TKey代表主键的类型。我们一般编写继承自IdentityUser、IdentityRole等的自定义类,可以增加自定义属性。
创建用户实体类User

// IdentityUser<long> 表示 long 类型主键的用户实体类
// IdentityUser 中定义了 UserName(用户名)、Email(邮箱)、PhoneNumber(手机号)、PasswordHash(密码的哈希值)等属性,
// 这里添加 CreationTime(创建时间)、NickName(昵称)两个属性。
public class User: IdentityUser<long>
{
	public DateTime CreationTime { get; set; }
	public string? NickName { get; set; }
}

创建角色实体类Role

using Microsoft.AspNetCore.Identity;

public class Role: IdentityRole<long>
{

}

2、NuGet安装对应包。
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.EntityFrameworkCore.Relational
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.Tools

3、创建继承自IdentityDbContext的上下文类

using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;

// 使用 Identity 框架要继承 IdentityDbContext
// IdentityDbContext 是一个泛型类,有3个泛型参数,分别代表用户类型、角色类型和主键类型
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);
	}
}	

4、修改 appsettings.json,添加数据库连接字符串。

{
  "Logging": {
	"LogLevel": {
	  "Default": "Information",
	  "Microsoft.AspNetCore": "Warning"
	}
  },
  "AllowedHosts": "*",
  "ConnectionStrings": {
	"Default": "Server=(localdb)\\mssqllocaldb;Database=IdentityMyDB;Trusted_Connection=True;MultipleActiveResultSets=true"
  }
}	

5、可以通过IdDbContext类来操作数据库,不过框架中提供了RoleManager、UserManager等类来简化对数据库的操作,部分方法的返回值为Task类型。

6、programs.cs文件,向依赖注入容器中注册标识框架相关的服务

using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// 向依赖注入容器中注册与标识框架相关的服务,并对相关的选项进行配置
// ----1. 数据库注入
IServiceCollection services = builder.Services;
services.AddDbContext<IdDbContext>(opt =>
{
	string connStr = builder.Configuration.GetConnectionString("Default")!;
	opt.UseSqlServer(connStr);
});
// ----2. 数据保护服务注入
// ----数据保护提供了一个简单、基于非对称加密改进的加密API用于确保Web应用敏感数据的安全存储
// ----不需要开发人员自行生成密钥,它会根据当前应用的运行环境,生成该应用独有的一个私钥
services.AddDataProtection();
// ----3. 添加标识框架的一些重要的基础服务
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;
});
// ----4. 注入 UserManager、RoleManager等服务
var idBuilder = new IdentityBuilder(typeof(User), typeof(Role), services);
idBuilder.AddEntityFrameworkStores<IdDbContext>()
	.AddDefaultTokenProviders()
	.AddRoleManager<RoleManager<Role>>()
	.AddUserManager<UserManager<User>>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
	app.UseSwagger();
	app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

7、执行以下命令进行数据迁移,执行后将在数据库中生成多张与 Identity 相关的表

Add-Migration InitIdentity
Update-database

8、创建相应的实体类

public record LoginRequest(string UserName, string Password);
public record SendResetPasswordTokenRequest(string Email);
public record ResetPasswordResponse(string Email, string Token, string NewPassword);

9、通过RoleManager、UserManager等来进行数据操作。比如创建角色、创建用户,用户验证,重置密码。

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Identity;

namespace IdentitySample.Controllers
{
	[ApiController]
	[Route("[controller]/[action]")]
	public class Test1Controller : ControllerBase
	{
		private readonly ILogger<Test1Controller> logger;
		private readonly RoleManager<Role> roleManager;
		private readonly UserManager<User> userManager;

		// 注入 RoleManager,UserManager,ILogger
		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()
		{
			// 1. 判断管理员角色是否存在,不存在则创建
			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);
				}
			}

			// 2. 创建账户
			User user = await userManager.FindByNameAsync("yzk");
			if (user == null)
			{
				user = new User
				{
					UserName = "yzk",
					Email = "51398898@qq.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();
		}

		// 登录
		[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
			{
				// 调用userManager的AccessFailedAsync方法来记录一次“登录失败”,
				// 当连续多次登录失败之后,账户就会被锁定一段时间,以避免账户被暴力破解
				var r = await userManager.AccessFailedAsync(user);
				if (!r.Succeeded)
				{
					return BadRequest("AccessFailed failed");
				}
				return BadRequest("Failed");
			}
		}

		// 发送重置密码 Token
		[HttpPost]
		public async Task<IActionResult> SendResetPasswordToken(
			SendResetPasswordTokenRequest req)
		{
			string email = req.Email;
			var user = await userManager.FindByEmailAsync(email);
			if (user == null)
			{
				return NotFound($"邮箱不存在:[{email}]");
			}
			// 调用GeneratePasswordResetTokenAsync方法来生成一个密码重置令牌,这个令牌会被保存到数据库中
			// 然后把这个令牌发送到用户邮箱
			string token = await userManager.GenerateEmailConfirmationTokenAsync(user);
			logger.LogInformation($"向邮箱{user.Email}发送Token={token}");
			return Ok(token); 
		}

		// 重置密码
		[HttpPost]
		public async Task<IActionResult> VerifyResetPasswordToken(
			ResetPasswordRequest req)
		{
			string email = req.Email;
			var user = await userManager.FindByEmailAsync(email);
			string token = req.Token;
			string password = req.NewPassword;
			var r = await userManager.ResetPasswordAsync(user, token, password);
			return Ok();
		}
	}
}	

重置密码流程
1、生成重置Token
2、Token发给客户(邮件、短信等),形式:链接、验证码等。
3、根据Token完成密码的重置。

posted @ 2024-07-31 23:38  小脑虎爱学习  阅读(115)  评论(0)    收藏  举报