ASP.NET Core 标识(Identity)框架系列(一):如何使用 ASP.NET Core 标识(Identity)框架创建用户和角色?

简介: ASP.NET Core 标识(Identity)框架系列(一):如何使用 ASP.NET Core 标识(Identity)框架创建用户和角色?

前言

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

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

  1. 用户管理:创建、管理和验证用户,这样你可以轻松操作注册用户、登录、注销、密码重置等功能。
  2. 角色管理:定义不同的用户角色,并将用户分配到这些角色中,这样你可以更好地控制用户的权限和访问级别。
  3. 密码策略:框架提供了密码策略功能,允许你定义密码的复杂度要求,例如密码长度、大小写字母、数字等要求。
  4. 外部登录:框架还支持你使用外部身份验证提供者(如 QQ、微信、微博等)进行身份验证。
  5. 电子邮件确认:通过电子邮件确认用户注册和密码重置操作,这样注册和修改密码操作更加安全可靠。

今天,我们主要聊聊如何使用 ASP.NET Core 标识(Identity)框架来管理用户和角色,也有涉及到密码策略、电子邮件确认等方面。

  1. 创建一个 ASP.NET Core webapi 项目,命名为 IdentitySample
  2. 引用以下 Nuget 包:

Microsoft.AspNetCore.Identity.EntityFrameworkCore

Microsoft.EntityFrameworkCore.Relational

Microsoft.EntityFrameworkCore.SqlServer

Microsoft.EntityFrameworkCore.Tools

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

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

4.创建用户实体类 User(重点看注释

using Microsoft.AspNetCore.Identity;
// 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; }
}

5.创建角色实体类 Role

using Microsoft.AspNetCore.Identity;
public class Role: IdentityRole<long>
{
}

6.创建继承自 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);
  }
}

7.打开 Program.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();

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

Add-Migration InitIdentity
Update-database

9.创建相应的实体类

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

10.在控制器中编写代码,操作角色、用户数据(重点看注释

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

11.启动项目,通过 Postman 或 Swagger 运行 API 进行测试


相关文章
|
11天前
|
开发框架 前端开发 .NET
VB.NET中如何利用ASP.NET进行Web开发
在VB.NET中利用ASP.NET进行Web开发是一个常见的做法,特别是在需要构建动态、交互式Web应用程序时。ASP.NET是一个由微软开发的开源Web应用程序框架,它允许开发者使用多种编程语言(包括VB.NET)来创建Web应用程序。
39 5
|
2月前
|
存储 开发框架 算法
ASP.NET Core 标识(Identity)框架系列(四):闲聊 JWT 的缺点,和一些解决思路
ASP.NET Core 标识(Identity)框架系列(四):闲聊 JWT 的缺点,和一些解决思路
|
2月前
|
开发框架 JSON .NET
ASP.NET Core 标识(Identity)框架系列(三):在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证
ASP.NET Core 标识(Identity)框架系列(三):在 ASP.NET Core Web API 项目中使用标识(Identity)框架进行身份验证
|
2月前
|
开发框架 .NET 开发工具
【Azure 应用服务】App Service 的.NET Version选择为.NET6,是否可以同时支持运行ASP.NET V4.8的应用呢?
【Azure 应用服务】App Service 的.NET Version选择为.NET6,是否可以同时支持运行ASP.NET V4.8的应用呢?
|
2月前
|
存储 开发框架 JSON
ASP.NET Core 标识(Identity)框架系列(二):使用标识(Identity)框架生成 JWT Token
ASP.NET Core 标识(Identity)框架系列(二):使用标识(Identity)框架生成 JWT Token
|
存储 缓存 .NET
一起谈.NET技术,提高ASP.NET应用程序性能的十大方法
  一、返回多个数据集   检查你的访问数据库的代码,看是否存在着要返回多次的请求。每次往返降低了你的应用程序的每秒能够响应请求的次数。通过在单个数据库请求中返回多个结果集,可以减少与数据库通信的时间,使你的系统具有扩展性,也可以减少数据库服务器响应请求的工作量。
1222 0
|
.NET
一起谈.NET技术,ASP.NET 4.0 一些隐性的扩展
  ASP.NET 4.0在很多方面都做了改进,在这篇ASP.NET 4.0白皮书就描述了很多ASP.NET 4.0的机制改变和改进。在我的博客中,也有几篇关于ASP.NET4.0的特性修改的文章。但是作为一个全新的框架和运行时,内部肯定还会有很多API和扩展点不会暴露的那么明显。
843 0
|
Web App开发 .NET 数据安全/隐私保护
一起谈.NET技术,ASP.NET身份验证机制membership入门——项目
  前面说了很多关于membership的内容,感觉内容有点凌乱,内容都是一个个知识点,下面我们通过一个小的项目,来把所有的相关内容串一下。   首先描述一下需求:   我们要做一个最简单的网站。有三类用户:匿名用户,员工,管理员,网站结构如下:        admin目录下的页面只允许admin角色的用户访问,employee目录下的页面只允许emp角色的用户访问。
1104 0
|
前端开发 .NET
一起谈.NET技术,ASP.NET MVC 2扩展点之Model Binder
  Model Binder在Asp.net MVC中非常简单。简单的说就是你控制器中的Action方法需要参数数据;而这些参数数据包含在HTTP请求中,包括表单上的Value和URL中的参数等。而ModelBinder的功能就是将这些个表单上的Value和URL中的参数换成对象,然后将这些对象绑定到Action的参数上面。
1056 0
|
Web App开发 前端开发 .NET
一起谈.NET技术,13个不可不知的ASP.NET MVC扩展点
ASP.NET MVC设计的主要原则之一是可扩展性。处理管线(processing pipeline)上的所有(或大多数)东西都是可替换的。因此,如果您不喜欢ASP.NET MVC所使用的约定(或缺乏某些约定),您可以创建自己的服务来支持您的约定,并将其注入到主管线中。
1232 0
下一篇
无影云桌面