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號