asp.net core 8 Authentication与Authorization
一、核心概念区分
- 认证(Authentication):验证用户的身份,确认 "你是谁"。
例如:用户输入用户名密码、使用 OAuth2 登录(如谷歌 / 微软账号)等,系统确认该用户的身份合法性。 - 授权(Authorization):在认证通过后,决定用户 "能做什么"。
例如:普通用户只能查看数据,管理员可以修改数据,系统通过授权规则限制访问权限。
1.1 标识框架
标识框架中提供了IdentityUser<TKey>
、IdentityRole<TKey>
两个实体类型,其中的TKey代表主键的类型,因此IdentityUser<Guid>
就代表使用Guid类型主键的用户实体类。我们可以在开发的时候直接使用IdentityUser<Guid>
等类型,不过使用起来比较麻烦,因为每次都要声明主键的泛型类型,而且我们一般还需要为实体类增加额外的属性。因此我们一般编写继承自IdentityUser<TKey>
、IdentityRole<TKey>
等的自定义类。
第1步,安装 Microsoft.AspNetCore.Identity.EntityFrameworkCore
创建ASP.NETCoreWebAPI项目,并通过NuGet安装Microsoft.AspNetCore.Identity.EntityFrameworkCore
。
第2步,创建用户实体类User和角色实体类Role
我们使用自增标识列类型的主键,因此我们编写分别继承自IdentityUser<long>
、IdentityRole<long>
的User类和Role类
除了IdentityUser
和IdentityRole
之外,标识框架中还有很多其他实体类,比如IdentityRoleClaim、IdentityUserClaim、IdentityUserLogin、IdentityUserToken等,一般情况下,我们不需要再编写这些实体类的子类。这些实体类有默认的表名,如果需要修改默认的表名或者对实体类进行进一步的配置,我们可以用EFCore中提供的IEntityTypeConfiguration来对实体类进行配置。
第3步,创建继承自IdentityDbContext
的类
这是一个EFCore中的上下文类,我们可以通过这个类操作数据库。IdentityDbContext
是一个泛型类,有3个泛型参数,分别代表用户类型、角色类型和主键类型。
namespace Identity框架
{
public class MyDbContext : IdentityDbContext<MyUser, MyRole, long>
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
}
}
}
我们可以直接通过MyDbContext
类来操作数据库,不过标识框架中提供了RoleManager、UserManager等类来简化对数据库的操作,这些类封装了对IdentityDbContext的操作。
标识框架中的方法有执行失败的可能,比如重置密码可能由于密码太简单而失败,因此标识框架中部分方法的返回值为Task<IdentityResult>
类型。IdentityResult
类型中有bool类型的Succeeded属性表示操作是否成功;如果操作失败,我们可以从Errors属性中获取错误的详细信息,由于有可能有多条错误信息,因此Errors是一个IEnumerable<IdentityError>
类型的属性。IdentityError类包含Code(错误码)和Description(错误的详细信息)这两个属性。
第4步,注册与标识框架相关的服务
我们需要向依赖注入容器中注册与标识框架相关的服务,并且对相关的选项进行配置
builder.Services.AddDbContext<MyDbContext>(ops =>
{
ops.UseSqlServer("server=.;database=Identity;uid=sa;pwd=[XXXXXX];TrustServerCertificate=True;");
});
builder.Services.AddDataProtection(); //安全地加密、解密应用程序中的敏感数据
builder.Services.AddIdentityCore<MyUser>(options => //注意不是Addldentity,Addldentity是传统MVC的
{
options.Password.RequireDigit = false; //表示不再强制要求密码中包含数字。
options.Password.RequireNonAlphanumeric = false; //表示不再强制要求密码中包含非字母数字字符(如 !@#$%^&* 等特殊符号)。
options.Password.RequireLowercase = false; //表示不再强制要求密码中包含小写字母。
options.Password.RequireUppercase = false; //表示不再强制要求密码中包含大写字母。
options.Password.RequiredLength = 6; //表示密码的最小长度为6。
options.Tokens.PasswordResetTokenProvider = TokenOptions.DefaultEmailProvider; //表示密码重置令牌提供程序的默认值。这意味着当用户请求重置密码时,系统会生成一个通过电子邮件发送的令牌,用户通过该令牌完成密码的重置操作。
options.Tokens.EmailConfirmationTokenProvider = TokenOptions.DefaultEmailProvider; //表示电子邮件确认令牌提供程序的默认值。即当用户注册新账户后,系统会生成一个用于确认邮箱的令牌,并通过电子邮件发送给用户,用户点击链接或输入令牌以完成邮箱验证。
});
//IdentityBuilder是ASP.NET Core Identity提供的一个核心类,用于配置用户和角色相关的服务和功能。
//1. typeof(MyUser):指定了应用程序中使用的用户类型,这里是自定义的MyUser类,通常继承自IdentityUser,用于表示系统中的用户。
//2. typeof(MyRole):指定了应用程序中使用的角色类型,这里是自定义的MyRole类,通常继承自IdentityRole,用于表示系统中的角色。
var idBuilder = new IdentityBuilder(typeof(MyUser), typeof(MyRole), builder.Services);
/**
* 这段代码通过IdentityBuilder配置了ASP.NET Core Identity框架,使其能够使用自定义的用户和角色类型(MyUser和MyRole),
* 并将数据存储在Entity Framework Core的MyDbContext中。同时,它还注册了必要的管理器(UserManager和RoleManager)和默认的令牌提供程序,
* 以支持用户和角色的全面管理功能,包括身份验证、授权、密码重置等常见操作。这些配置是构建安全、可扩展的身份验证和授权系统的基础。
*/
idBuilder.AddEntityFrameworkStores<MyDbContext>()
.AddDefaultTokenProviders()
.AddRoleManager<RoleManager<MyRole>>()
.AddUserManager<UserManager<MyUser>>();
因为过于简单的密码会带来系统的安全风险,所以标识框架默认对于密码的复杂度有苛刻的要求,比如密码中必须同时含有至少一个大写字母、一个小写字母、一个数字、一个特殊符号。这样严格的密码要求虽然能提高系统的安全性,但是用户用起来会非常麻烦。如果综合考虑项目的运营策略和安全等级等要求,需要调整这个密码的限制的话,我们就可以像第9~15行代码那样进行设置。我们在例子中设置的是非常宽松的密码要求:不要求小写字母、不要求大写字母、不要求数字、不要求特殊符号、长度最短为6,而且重置密码和确认邮箱的验证码也采用比较简单的生成值,便于用户输人。需要注意的是,这里的设置只是一个例子而已,读者需要根据项目的情况进行个性化的设置,以免由于密码要求太宽松而影响系统安全性。
AddEntityFrameworkStores<MyDbContext>()
:
这个方法将Entity Framework Core作为存储后端,用于持久化用户和角色数据。<MyDbContext>
指定了应用程序使用的DbContext类,这里是自定义的MyDbContext
,通常继承自IdentityDbContext<MyUser, MyRole, ...>
。通过这个方法,Identity会将用户和角色的数据存储到数据库中,并处理相关的数据库操作,如用户的创建、查询、更新和删除等。
AddDefaultTokenProviders()
:
该方法为Identity添加了默认的令牌提供程序。这些令牌用于多种用途,比如用户账户的电子邮件确认、密码重置等。默认的令牌提供程序生成安全的令牌,这些令牌通过电子邮件或其他方式发送给用户,以验证其身份或执行特定的操作。
AddRoleManager<RoleManager<MyRole>>()
:
这个方法向服务容器中注册了一个RoleManager<MyRole>
实例,用于管理角色。RoleManager
提供了创建、删除、查找和验证角色等功能。通过泛型参数MyRole
,指定了应用程序中使用的角色类型。这使得应用程序能够使用角色来对用户进行分组和权限管理。
AddUserManager<UserManager<MyUser>>()
:
最后,这个方法向服务容器中注册了一个UserManager<MyUser>
实例,用于管理用户。UserManager
提供了创建、删除、查找、验证用户以及管理用户密码、锁定状态等功能。通过泛型参数MyUser
,指定了应用程序中使用的用户类型。这使得应用程序能够高效地管理用户账户及其相关操作。
第5步,生成表
通过执行Add-Migration、Update-database等命令执行EFCore的数据库迁移,然后程序就会在数据库中生成多张数据库表,这些数据库表都由标识框架负责管理,开发人员一般不需要直接访问这些表。
安装命令行工具
PM> Add-Migration Init
Add-Migration : 无法将“Add-Migration”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。
所在位置 行:1 字符: 1
+ Add-Migration Init
+ ~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Add-Migration:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
.net 8 需要安装 Microsoft.EntityFrameworkCore.Tools
然后,项目现在的包,
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.19" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.19" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.19" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.19">
先执行
PM> Add-Migration Init
Build started...
Build succeeded.
To undo this action, use Remove-Migration.
再执行
PM> Update-Database
Build started...
Build succeeded.
Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (10ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT 1
Microsoft.EntityFrameworkCore.Database.Command[20101]
Executed DbCommand (9ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
SELECT OBJECT_ID(N'[__EFMigrationsHistory]');
Microsoft.EntityFrameworkCore.Database.Command[20101]
....
第6步,MyUser添加字段
public class MyUser: IdentityUser<long>
{
public DateTime CreateAt { get; set; }
}
Add-Migration CreateAt
生成
20250830115815_CreateAt.cs
再Update-Database
第7步,添加用户和角色
添加控制器
Test1Controller.cs
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Data;
using System;
namespace Identity框架.Controllers
{
[ApiController]
[Route("[controller]")]
public class Test1Controller : ControllerBase
{
private readonly RoleManager<MyRole> roleManager;
private readonly UserManager<MyUser> userManager;
public Test1Controller(RoleManager<MyRole> roleManager, UserManager<MyUser> userManager)
{
this.roleManager = roleManager;
this.userManager = userManager;
}
[HttpPost]
public async Task<IActionResult> CreateUser()
{
bool roleExists = await roleManager.RoleExistsAsync("admin");
if (!roleExists)
{
MyRole role = new MyRole { Name = "Admin" };
var r = await roleManager.CreateAsync(role); //AspNetRoles
if (!r.Succeeded)
{
return BadRequest(r.Errors);
}
}
MyUser user = await this.userManager.FindByNameAsync("tss");
if (user == null)
{
user = new MyUser { UserName = "tss", Email = "tangge@vip.qq.com", EmailConfirmed = true };
var r = await userManager.CreateAsync(user, "123456"); //AspNetUsers
if (!r.Succeeded)
{
return BadRequest(r.Errors);
}
r = await userManager.AddToRoleAsync(user, "admin"); //AspNetUserRoles
if (!r.Succeeded)
{
return BadRequest(r.Errors);
}
}
return Ok();
}
}
}