C# Web开发教程(九)标识框架[Identity]
标识(Identity)框架
Authentication和Authorization
- Authentication: 认证,你是谁
- Authorization: 权限,你有什么权限
1、标识(Identity)框架: 采用基于角色(Role)的访问控制
- (Role-Based Access Control,简称RBAC)策略,内置了对用户、角色等表的管理以及相关的接口,支持外部登录、2FA等。
- 标识框架使用EF Core对数据库进行操作,因此标识框架支持几乎所有数据库。
Identity框架使用
- IdentityUser<TKey>、IdentityRole<TKey>,TKey代表主键的类型。我们一般编写继承自ldentityUser<TKey>、IdentityRole<TKey>等的自定义类,可以增加自定义属性。
- NuGet安装:
- Install-Package Microsoft.EntityFrameworkCore -Version 6.0.0
- Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 6.0.0
- Install-Package Microsoft.EntityFrameworkCore.Tools -Version 6.0.0
- Install-Package Microsoft.AspNetCore.Identity.EntityFrameworkCore -Version 6.0.0
- 创建继承自ldentityDbContext的类
- 可以通过ldDbContext类来操作数据库,不过框架中提供了RoleManager、UserManager等类来简化对数据库的操作。
- 部分方法的返回值为Task<IdentityResult>类型
// MyUser.cs
using Microsoft.AspNetCore.Identity;
namespace WebApplicationAboutIdentity
{
// 默认的 IdentityUser 使用 string 类型主键,这里改为 long
public class MyUser:IdentityUser<long>
{
}
}
// MyRole.cs
using Microsoft.AspNetCore.Identity;
namespace WebApplicationAboutIdentity
{
public class MyRole:IdentityRole<long>
{
}
}
// MyDbContext.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace WebApplicationAboutIdentity
{
// 继承 IdentityDbContext,指定自定义的用户类型、角色类型和主键类型
// 会自动创建 Identity 相关的所有表(Users、Roles、UserRoles 等)
public class MyDbContext : IdentityDbContext<MyUser,MyRole,long>
{
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
}
}
}
// DbContextDesignTimeFactory.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
namespace WebApplicationAboutIdentity
{
public class DbContextDesignTimeFactory: IDesignTimeDbContextFactory<MyDbContext>
{
public MyDbContext CreateDbContext(string[] args)
{
// 配置数据库连接,用于 EF Core 迁移命令
DbContextOptionsBuilder<MyDbContext> builder = new DbContextOptionsBuilder<MyDbContext>();
string connStr = "Server=.;Database=idtest1;Trusted_Connection=True;";
builder.UseSqlServer(connStr);
MyDbContext ctx = new MyDbContext(builder.Options);
return ctx;
}
}
}
// Program.cs
......
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using WebApplicationAboutIdentity;
var builder = WebApplication.CreateBuilder(args);
......
// 数据库配置
builder.Services.AddDbContext<MyDbContext>(opt =>
{
opt.UseSqlServer("Server=.;Database=idtest1;Trusted_Connection=True;");
});
// Identity 配置
builder.Services.AddDataProtection();
builder.Services.AddIdentityCore<MyUser>(options =>
{
// 简化密码策略
options.Password.RequireDigit = false;
options.Password.RequiredLength = 6;
options.Password.RequireLowercase = false;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
// 设置令牌提供程序
options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider;
options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider;
});
// Identity 构建器
// 指定用户和角色类型,配置使用 Entity Framework 存储,添加默认令牌提供程序,注册用户管理器和角色管理器
IdentityBuilder idBuilder = new(typeof(MyUser),typeof(MyRole),builder.Services);
idBuilder.AddEntityFrameworkStores<MyDbContext>().AddDefaultTokenProviders().AddUserManager<UserManager<MyUser>>().AddRoleManager<RoleManager<MyRole>>();
var app = builder.Build();
......
app.Run();
- 最后,作数据库迁移并更新数据库
- 小结:这个配置为应用程序提供了完整的用户认证和授权基础架构
- 自定义主键类型: 使用 long 代替默认的 string
- 简化的密码策略: 降低了密码复杂度要求
- 完整的 Identity 功能: 包含用户管理、角色管理、令牌生成等
- SQL Server 存储: 使用 Entity Framework Core 和 SQL Server

Identity Demo 演示---UserManager和RoleManager
- 演示接口: 使用 ASP.NET Core Identity 进行身份管理和授权的控制器代码
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
namespace WebApplicationAboutIdentity.Controllers
{
[Route("api/[controller]/[action]")]
[ApiController]
public class DemoController : ControllerBase
{
// 依赖注入
private readonly UserManager<MyUser> userManager;
private readonly RoleManager<MyRole> roleManager;
public DemoController(UserManager<MyUser> userManager, RoleManager<MyRole> roleManager)
{
this.userManager = userManager;
this.roleManager = roleManager;
}
[HttpPost]
public async Task<ActionResult<string>> Test1()
{
// 检查 "admin" 角色是否存在,如果不存在就创建该角色
if (await roleManager.RoleExistsAsync("admin") == false)
{
MyRole role = new() { Name = "admin" };
var result = await roleManager.CreateAsync(role);
if (!result.Succeeded) return BadRequest("roleManage.CreateAsync failed");
}
// 查找用户名为 "yzk" 的用户,如果用户不存在,创建新用户并设置密码为 "123456"
MyUser user1 = await userManager.FindByNameAsync("yzk");
if (user1 == null)
{
user1 = new() { UserName = "yzk" };
var result = await userManager.CreateAsync(user1, "123456");
if (!result.Succeeded) return BadRequest("userManage.CreateAsync failed");
}
// 检查user1是否已在 "admin" 角色中,如果不在,就将用户添加到该角色
if(!await userManager.IsInRoleAsync(user1, "admin"))
{
var result = await userManager.AddToRoleAsync(user1, "admin");
if (!result.Succeeded) return BadRequest("userManage.AddToRoleAsync failed");
}
return "ok";
}
}
}
- 演示接口: 完成用户密码验证
// CheckPwdRequest.cs
namespace WebApplicationAboutIdentity
{
// 使用 record 类型定义请求数据模型,这是一个不可变的数据传输对象
// 含两个属性:UserName(用户名)和 Password(密码)
// 用途: 匹配前端传过来的UerName和Password
public record CheckPwdRequest(string UserName, string Password);
}
// 主程序
......
[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 (hostEnvironment.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
{
// AccessFailedAsync 记录一次登录失败,达到阈值会自动锁定用户
await userManager.AccessFailedAsync(user);
return BadRequest("用户名或密码错误");
}
}
-
接口演示: 重置密码流程
- 生成重置Token,发给用户(短信,邮箱),用户根据Token完成密码的重置
- 说明事项: 为了演示实例的简单,发送的逻辑改为
控制台输出Token
- 说明事项: 为了演示实例的简单,发送的逻辑改为
[HttpPost] public async Task<ActionResult> SendResetPasswordToken(string userName) { var user = await userManager.FindByNameAsync(userName); if (user == null) { return BadRequest("用户名不存在"); } string token = await userManager.GeneratePasswordResetTokenAsync(user); Console.WriteLine($"验证码是{token}"); // 验证码是758683 return Ok(); } // Program.cs ...... builder.Services.AddIdentityCore<MyUser>(options => { ...... // 如果注释掉,Token就是一堆长长的字符串 options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider; ...... });- 用户带着
用户名,验证码和新密码,完成重置密码流程
[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.AccessFailedAsync(user); return BadRequest("密码重置失败"); } } - 生成重置Token,发给用户(短信,邮箱),用户根据Token完成密码的重置

浙公网安备 33010602011771号