当前位置: 首页 > news >正文

在ASP.NET Core WebApi中使用标识框架(Identity)

一.什么是标识框架

Identity框架是微软官方提供的一套完整的身份认证和授权框架,旨在简化用户管理(注册、登录、注销、角色权限)流程。它内置了常见的功能,比如密码哈希、邮箱确认、多因素认证等,同时支持自定义扩展。

因为我们的服务器是不能被随意地访问的,比如有的功能需要注册用户才能访问,有的功能需要管理员才能访问。针对资源的访问限制有两个概念:Authentication与Authorization,即鉴权与授权

  • Authentication:用来对访问者的用户身份进行验证;
  • Authorization:用来验证访问者的用户身份是否有对资源进行访问的权限。

通俗来说,Authentication是用来验证“用户是否登录成功”的,Authorization是用来验证“用户是否有权限访问”的。

二.标识框架Identity

大部分系统中都需要通过数据库保存用户、角色等信息,并且需要注册、登录、密码重置、角色管理等功能。ASP.NET Core提供了标识(identity)框架,它采用RBAC(role-based access control,基于角色的访问控制)策略,内置了对用户、角色等表的管理及相关的接口,从而简化了系统的开发。标识框架还提供了对外部登录的支持。


标识框架中提供了IdentityUser<TKey>IdentityRole<TKey>两个实体类型,其中的TKey代表主键的类型,因此IdentityUser<Guid>就代表使用Guid类型主键的用户实体类。

1.环境搭建

创建ASP.NET Core Web API项目(.net8),并通过NuGet安装

包名称用途
Microsoft.AspNetCore.Identity.EntityFrameworkCoreASP.NET Core Identity 用户管理,基于 EF Core
Microsoft.EntityFrameworkCore.SqlServer使用 SQL Server 作为存储数据库(换数据库类型可换包)
Microsoft.AspNetCore.Authentication.JwtBearerJWT Bearer 认证中间件
Microsoft.EntityFrameworkCore.Tools支持数据库迁移命令,比如 Add-MigrationUpdate-Database

dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore --version 8.0.17
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package Microsoft.EntityFrameworkCore.Tools


2.创建用户实体类User和角色实体类Role

public class User : IdentityUser<long>
{public DateTime CreationTime { get; set; }public string? NickName { get; set; }
}
public class Role : IdentityRole<long>
{
}

IdentityUser中定义了UserName(用户名)、Email(邮箱)、PhoneNumber(手机号)、PasswordHash(密码的哈希值)等属性,我们在User中又添加了CreationTime(创建时间)、NickName(昵称)两个属性(虽然接口本身就有很多基本的字段,但是我们有可能还需要一些其他字段,所以通过继承实现了子类来方便我们添加字段)。

除了IdentityUser和IdentityRole之外,标识框架中还有很多其他实体类,比如IdentityRoleClaim、IdentityUserClaim、IdentityUserLogin、IdentityUserToken等,一般情况下,我们不需要再编写这些实体类的子类。这些实体类有默认的表名,如果需要修改默认的表名或者对实体类进行进一步的配置,我们可以用EF Core中提供的IEntityTypeConfiguration来对实体类进行配置

3.创建继承自IdentityDbContext的类

这是一个EF Core中的上下文类,我们可以通过这个类操作数据库。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);}
}

我们可以直接通过IdDbContext类来操作数据库,不过标识框架中提供了RoleManager、UserManager等类来简化对数据库的操作,这些类封装了对IdentityDbContext的操作。

4.注册与标识框架相关的服务,并且对相关的选项进行配置

 //添加数据包含服务builder.Services.AddDataProtection();builder.Services.AddIdentityCore<User>(options =>{// 密码必须包含数字?options.Password.RequireDigit = false;// 密码必须包含小写字母?options.Password.RequireLowercase = false;// 密码必须包含非字母数字字符?(比如 @、# 等符号)options.Password.RequireNonAlphanumeric = false;// 密码必须包含大写字母?options.Password.RequireUppercase = false;// 密码最小长度options.Password.RequiredLength = 6;// 配置密码重置时,使用哪种 Token 生成器options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;// 配置邮箱确认时,使用哪种 Token 生成器options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;});// 创建 IdentityBuilder 对象,指定用户类型和角色类型,并传入 DI 容器var idBuilder = new IdentityBuilder(typeof(User), typeof(Role), builder.Services);//用 IdDbContext 作为 Identity 存储层(一般是继承自 IdentityDbContext<User, Role, ...> 的自定义类)idBuilder.AddEntityFrameworkStores<IdDbContext>()//注册 内置的默认 Token 生成器.AddDefaultTokenProviders()//把 角色管理服务 RoleManager<Role> 注册进 DI 容器.AddRoleManager<RoleManager<Role>>()//把 用户管理服务 UserManager<User> 注册进 DI 容器.AddUserManager<UserManager<User>>();

在配置文件中写入数据库连接字符串。

"ConnectionStrings": {"Default": "Data Source=.;Database=Customers;User ID=sa;Password=123456;MultipleActiveResultSets=True;"
}
 "ConnectionStrings": {"DefaultConnection": "Server=(local);Database=IdDb;Trusted_Connection=True;TrustServerCertificate=True;"}
 builder.Services.AddDbContext<IdDbContext>(options =>{options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));});

5.执行数据库迁移

Add-Migration createIdentityUpdate-database等命令执行EF Core的数据库迁移,然后程序就会在数据库中生成多张数据库表。这些数据库表都由标识框架负责管理,开发人员一般不需要直接访问这些表。

6.编写控制器的代码

我们在控制器中需要对角色、用户进行操作,也需要输出日志,因此通过控制器的构造方法注入相关的服务。

 [Route("api/[controller]")][ApiController]public class TestController : ControllerBase{private readonly ILogger<TestController> _logger;private readonly UserManager<User> _userManager;private readonly RoleManager<Role> _roleManager;public TestController(ILogger<TestController> logger, UserManager<User> userManager,RoleManager<Role> roleManager){_logger = logger;_userManager = userManager;_roleManager = roleManager;}       }

7.Identity 框架中的方法返回结果:IdentityResult

在 ASP.NET Core Identity 标识框架中,很多用户管理相关的方法,比如:

  • 用户创建

  • 密码修改

  • 密码重置

  • 邮箱确认

  • 角色添加/删除

都不直接抛异常,而是通过返回一个 IdentityResult 对象来告诉你操作是否成功。

(1)IdentityResult 的特点:

属性/特性说明
Succeeded一个 bool 类型,表示操作是否成功。通常用于快速判断。
Errors一个 IEnumerable<IdentityError> 类型,存放失败时的详细错误列表。
 (2)为什么要用 IdentityResult?

 因为:

  • Identity 的操作涉及用户数据、密码复杂度、Token 验证、数据库写入等各种可能失败的情况。

  • 比起直接抛异常,这种返回对象方式更适合可控错误处理。

(3)IdentityError 结构:

每个失败原因,用一个 IdentityError 对象表示,包含:

属性说明
Code错误码(可用于逻辑判断或多语言翻译)
Description用户可读的错误描述,比如:“Passwords must be at least 6 characters.”
(4) 示例
var result = await _userManager.ResetPasswordAsync(user, token, newPassword);if (result.Succeeded)
{// 成功处理
}
else
{foreach (var error in result.Errors){Console.WriteLine($"错误码: {error.Code}, 描述: {error.Description}");}
}

8.示例(仅用来熟悉常用API)

(1)编写创建角色和用户的方法。
[HttpPost]
public async Task<IActionResult> CreateUserRole()
{// 判断角色 "Admin" 是否存在bool roleExists = await _roleManager.RoleExistsAsync("Admin");if (!roleExists){// 不存在则创建角色Role role = new Role { Name = "Admin" };var r = await _roleManager.CreateAsync(role);if (!r.Succeeded)  // 创建失败,返回 400 和错误信息{return BadRequest(r.Errors);}}// 查找用户名为 "ZhangSan" 的用户User user = await _userManager.FindByNameAsync("ZhangSan");if (user == null){// 不存在则创建新用户,设置用户名、邮箱并确认邮箱user = new User{UserName = "ZhangSan",Email = "3456789@qq.com",EmailConfirmed = true,};var r = await _userManager.CreateAsync(user, "123456"); // 创建用户并设置密码if (!r.Succeeded)  // 创建失败返回错误信息{return BadRequest(r.Errors);}// 将用户添加到 "Admin" 角色r = await _userManager.AddToRoleAsync(user, "Admin");if (!r.Succeeded)  // 添加角色失败返回错误信息{return BadRequest(r.Errors);}}return Ok();  // 成功返回 200
}
(2)编写处理登录请求的操作方法Login
public record LoginRequest(string UserName, string Password);
  [HttpPost]public async Task<IActionResult> Login(LoginRequest loginRequest){string userName = loginRequest.UserName;string password = loginRequest.Password;// 根据用户名查找用户var user = await _userManager.FindByNameAsync(userName);if (user == null){// 用户不存在,返回 404return NotFound($"用户名{userName}不存在!");}// 判断用户是否被锁定(连续登录失败导致)var islocked = await _userManager.IsLockedOutAsync(user);if (islocked){// 用户锁定,返回 400,提示锁定信息return BadRequest("用户已锁定!");}// 验证密码是否正确var success = await _userManager.CheckPasswordAsync(user, password);if (success){//重置访问失败计数await _userManager.ResetAccessFailedCountAsync(user);// 密码正确,登录成功,返回 200return Ok();}else{// 密码错误,记录一次失败尝试(用于锁定机制)var r = await _userManager.AccessFailedAsync(user);if (!r.Succeeded){// 记录失败信息失败,返回错误return BadRequest("访问失败信息写入错误!");}else{// 普通密码错误返回 400return BadRequest("失败!");}}}

学自杨中科老师,可搭配老师b站视频学习.

相关文章:

  • 镇江网站制作哪家好口碑营销案例ppt
  • 网站建设专员今日重大事件
  • 重庆动画网站建设百度极速版客服人工在线咨询
  • 我的世界做神器指令网站百度seo免费推广教程
  • 做拍卖网站需要多少钱新闻头条最新消息今天
  • 做竞争小的网站搜索引擎优化的方法
  • 对象实例化内存布局与访问定位
  • spring项目启动sheel脚本
  • SpringBoot 数据库连接池与 ManticoreSearch 兼容性测试
  • 本地如何安装midscene.js运行环境
  • Liunx操作系统笔记2
  • 【AI论文】从跨领域视角重新审视强化学习在大型语言模型推理中的应用
  • 【实时Linux实战系列】基于实时Linux的音频处理应用开发
  • BGP边界网关协议
  • 深度图聚类DGC—Paper Notes
  • Windows所有系统自带.NET Framework版本win7,win10,win11预装.NET版本
  • CommunityToolkit.Mvvm 重构激光直写控制软件
  • Jenkins 常用定时构建脚本
  • 电商导购app平台的缓存策略与性能优化方案:架构师的实践经验
  • 将Python Tkinter程序转换为手机可运行的Web应用 - 详细教程
  • 激光束修复手机屏任意层不良区域,实现液晶线路激光修复原理
  • wordpress Contact Form 7表单插件设置使用教程
  • Hoare逻辑与分离逻辑:从程序验证到内存推理的演进
  • 智能光学计算成像技术与应用
  • 鸿蒙Next仓颉开发语言中的数据类型总结分享
  • 初识Tomcat