跳到主要内容

Identity 领域能力指南

Identity 模块(src/Services/Identity/FreeKit.Identity)基于 ASP.NET Core Identity + FreeSql 构建,提供一整套用户、角色、权限、租户、组织架构、设备与密钥的建模与运行时能力。本文档面向开发者,聚焦"模块提供什么能力、概念如何关联、为什么这样设计、在业务代码里怎么调用",不罗列实体字段(实体细节可在源码 FreeKit.Identity/Models 中查阅)。

如果你要的是某一块的深度用法,请跳到对应文档:

1. 模块边界与子域划分

Identity 不是"一个用户表 + 一堆接口",而是一组围绕"身份与访问"的内聚子域。它们共享同一个身份主体 IdentityUser,但在职责边界上彼此分离:

子域负责什么不负责什么
认证(Account)登录/注册/注销/刷新 Token/模拟/邮箱校验改密不直接做权限判定(交给 Permission 子域)
用户(User/Profile)跨身份基础查询、管理员锁定/重置密码、当前用户资料/头像/改密/改用户名不提供"用户 CRUD",创建走注册、员工维度走 Employee
角色(Role)角色定义、默认/公开/静态角色、数据范围不持有权限定义本身
权限(Permission)权限树、赋权(用户/角色/岗位)、权限检查、复制不负责登录
租户(Tenant)租户隔离、切换、邀请成员不负责用户注册
组织架构(OrgUnit/Position/Employee)部门树、岗位、员工档案与调岗不负责认证
设备(Device)登录设备指纹、信任、新设备风控不独立成服务,内嵌于登录流程
双因素(2FA)启用/确认/禁用、登录验证依赖邮箱通道
安全(AccessKey/OnlineUser/UserToken)API 密钥、在线用户、Token 吊销、请求日志
社交登录(OAuth2/OpenIddict)第三方绑定/解绑/登录、OIDC 客户端管理
个性化(Shortcut/TableColumnSetting)用户快捷菜单、表格列设置

设计要点:没有独立的"用户 CRUD"控制器。 UserController/api/identity/users)只提供列表与详情查询;创建用户走注册(AccountController.register),资料修改走 ProfileController,员工维度的管理走 EmployeeController。这是有意把"用户生命周期"拆分到不同职责边界——注册、资料、员工档案各自演进,互不耦合。

2. 核心关系

下面是模块内核心概念的关联关系(基于真实实体导航与代码证据,非示意):

要点解读:

  • 权限的授予对象是多态的PermissionGrantModels/Identity/PermissionGrant.cs)用 PermissionGrantType 区分 R(角色)/U(用户)/P(岗位),统一挂在 Permission 上。这意味着同一个权限可分别授给用户、角色或岗位,解析时三条来源合并生效。
  • 组织关系走"员工档案"而非用户实体本身IdentityUser 只暴露 OrgUnits/Positions 集合用于便捷查询;真正承载"某人在某部门任某岗"的是 EmployeeInfoUserId + OrgUnitId + PositionId + LeaderUserId)。
  • 设备/密钥/Token 都是用户的从属UserLoginDeviceUserAccessKeyIdentityUserTokenRefreshToken 都以 UserId 为外键,删除用户时相关凭据一并失效。
  • 租户是多对多的软边界IdentityUser.Tenants 集合 + IdentityTenant 表,配合中间件 IdentityTenantResolver 在请求期确定当前租户。

3. 各子域:设计动机与业务能力

下面按子域展开"为什么这样设计"和"它提供什么业务能力"。所有动机均对应代码证据。

3.1 认证(Account)

  • 能力:密码登录、邮箱验证码登录、注册、注销、刷新 Token、管理员模拟、邮箱校验与改密、验证码。
  • 动机——生命周期拆分:注册是用户进入系统的唯一写入口(AccountService.RegisterAsync),登录只做校验与签发,不做用户创建。管理员模拟(AccountService.ImpersonateAsync)会保留 impersonator 与原始租户信息写入 Token Claim,便于审计与回退。
  • 动机——验证与签发分离ITokenHandler 专职签发/校验 JWT,IRefreshTokenService 管理刷新令牌轮换,AccountService 只做编排。这样 Token 策略(有效期、刷新窗口)可独立演进。

3.2 用户与资料(User / Profile)

  • 能力:跨身份基础查询(SSO 选人、选主管、按 Id 查找)、当前用户资料、头像、昵称、改密码、改用户名。
  • 动机——查询与写入分离IUserService 只提供只读查询(UserController 仅两个 HttpGet);写操作被拆到 ProfileService(改资料/头像/密码/用户名)和注册流程。在领域事件层面,IdentityUser 实现了 IDomainEventBase,会在创建/变更/硬删除/登录时抛出 UserCreateDomainEvent / UserChangeDomainEvent / UserHardDeleteDomainEvent / UserLastLoggedInAtDomainEvent,由对应 Handler 落地(如登录时间更新、会员档案联动)。

3.3 角色(Role)

  • 能力:角色 CRUD、默认角色(新用户注册自动分配)、公开角色(其他用户可见)、静态角色(内置不可删)、数据范围(DataScope)。
  • 动机——角色是权限的容器:角色本身不定义权限,只通过 PermissionGrant(R) 关联权限树节点。默认角色让"注册即赋予基础权限"成为配置而非代码;静态角色防止被误删导致授权链断裂。

3.4 权限(Permission)

  • 能力:权限树(自引用 ParentId/ChildrenPermissionTypeFolder/Menu/Element/Api)、赋权(用户/角色/岗位)、权限检查、角色/用户权限复制。
  • 动机——权限定义与代码强一致(关键设计)PermissionManager.InitPermissionAsync() 在启动时调用 PermissionUtil.GetFreeKitAuthorizeAttributes(),通过反射扫描所有 Controller[Authorize(Policy=...)][KitAuthorize] 特性,把每个受保护接口同步进权限树。这样权限点不会因代码改动而"漏配"或"漂移"——你加了带 Policy 的接口,权限树就多一个节点;手工维护成本降到最低。解析时 Admin 角色直接放行,其余查 PermissionGrant 的 U/R/P 三来源合并判定。

3.5 租户(Tenant)

  • 能力:租户 CRUD、切换(返回换租户后的新 Token)、邀请成员、成员列表。
  • 动机——多租户隔离优先级明确IdentityTenantResolver.Resolve(HttpContext) 的解析顺序为 X-Tenant-Id 请求头 > JWT 中的 tenant_id 声明。请求头优先,方便服务端间调用显式指定租户;登录态下回落到 Token 声明,保证用户自身数据隔离。切换租户会重新签发 Token 并把新 tenant_id 写入 Claim(TenantService)。

3.6 组织架构(OrgUnit / Position / Employee)

  • 能力:组织单元树(ParentId 自引用,编码 Code 按层级自动生成如 00001.00042.00005)、职位(绑定 OrgUnitId、编制 HeadCount、是否主管岗)、员工档案(部门/岗位/直属上级/工号/在职状态)及调岗。登录锁定与管理员密码重置属于用户能力。
  • 动机——组织是"视图"而非"身份":用户身份(IdentityUser)稳定,而人在组织里的位置(EmployeeInfo)会变化。把员工维度独立出来,使调岗/离职不影响登录身份,也避免用户表被组织字段污染。

3.7 设备(Device)

  • 能力:登录设备指纹记录、信任判定、新设备风控;内嵌于登录流程,无独立服务类。
  • 动机——新设备风控:设备指纹 = SHA256(UserAgent) 前 32 位(UserLoginDeviceManager),IP 变化不影响识别。信任有效期由 Device:TrustExpireDays(默认 180 天)控制;新设备行为由设置 Device.NewDeviceMode(默认 notify)决定——notify 仅发提醒邮件不阻断,verify 要求邮箱验证码(AccountService 登录时读取该值决策)。信任过期后下次登录重新验证。

3.8 双因素(2FA)

  • 能力:启用(发确认邮件)、确认、禁用、登录验证。
  • 动机——与设备风控共用"二次验证"出口:2FA 开启时,登录一律要求验证码(TwoStepVerificationService 通过 emailFactory.Send2faEmailAsync / SendEnable2faEmailAsync 发码)。它与"未受信设备"共用同一套 VerifyToken 验证通道,逻辑统一。

3.9 安全(AccessKey / OnlineUser / UserToken)

  • 能力:AccessKey 管理(UserAccessKeyAccessKeyId/Secret、状态、最近使用时间、操作日志 UserAccessKeyLog)、在线用户、UserToken 吊销、请求日志。
  • 动机——服务间调用的稳定身份:AccessKey 给非浏览器/脚本/微服务一套长期可用的凭证,与用户会话 Token 解耦;吊销 Token 只影响当前会话,不影响 AccessKey。

3.10 社交登录(OAuth2 / OpenIddict)

  • 能力:OAuth2 绑定/解绑/登录(GitHub/Gitee/QQ/微信/Google/Microsoft/KitSSO)、OpenIddict 客户端与应用管理。
  • 动机——身份来源多元化:第三方登录通过 IdentityUserLogin 绑定到同一 IdentityUser,保证"多种登录方式 = 同一个身份"。

3.11 个性化(Shortcut / TableColumnSetting)

  • 能力:用户快捷菜单、表格列设置。
  • 动机——界面偏好是用户态而非系统态:这些设置按用户隔离,属于身份主体的体验延伸。

4. 核心服务接口

开发者在业务代码里通常注入以下 应用服务(均实现 IApplicationService,来自 FreeKit.Identity.Application.*.Contracts):

  • IAccountService — 登录/注册/邮箱校验/改密/刷新 Token 的编排入口。
  • IUserService — 跨身份基础查询(SSO 选人、选主管、通用按 Id 查找),不负责写。
  • IProfileService — 当前用户资料、头像、昵称、改密码、改用户名。
  • IRoleService / IPermissionService / ITenantService / IOrgUnitService / IPositionService / IEmployeeService — 各自 CRUD(多数实现 ICrudAppService)。
  • IUserAccessKeyService / IOnlineUserService / IUserTokenService / IUserShortcutService / ITableColumnSettingService — 安全与个性化。
  • IOAuth2Service / IOAuth2AdminService — 社交登录与 OpenIddict 客户端。
  • ITwoStepVerificationService / IRefreshTokenService / IUserLoginService / ILinkUserService / ISerilogService / IRequestLogService

领域层(来自 FreeKit.Identity.Domain)的 Manager 提供更细粒度的建模能力,框架/内部使用较多:

  • IdentityUserManager(命名空间 FreeKit.Identity.Domain.Identity)— ASP.NET Identity 用户管理扩展。
  • IUserLoginDeviceManagerUserLoginDeviceManager)— 设备指纹信任、新设备处理、信任过期(Device:TrustExpireDays)。
  • IPermissionManager — 权限定义初始化与解析(启动时扫描控制器 [Authorize]/[KitAuthorize] 同步权限树)。
  • IdentityLinkUserManager / TwoStepVerificationService(内部聚合,依赖 IdentityUserManager + ITokenHandler)。

5. 配置(业务含义)

认证与 Token

{
"Authentication": {
"ExpiresTime": 86400, // AccessToken 有效期(秒),由 TokenHandler 读取
"RefreshExpiresIn": 2592000 // RefreshToken 有效期(秒)
}
}

TokenHandler 还会回退读取 Authentication:0:Schemes:ExpiresTime / Authentication:0:ExpiresIn 等多方案配置键。

设备信任

  • Device:TrustExpireDays(默认 180 天):信任设备有效期;到期需重新验证。由 UserLoginDeviceManager 读取。
  • 新设备行为由设置 Device.NewDeviceMode 控制,取值 notify(仅邮件通知)或 verify(要求邮箱验证码);默认 notifyLoginService 在登录时读取该值决定流程。

密码与锁定策略(IdentityOptions

IdentityModuleStartup 中通过 AddIdentityCore<IdentityUser> 配置,等价于标准 ASP.NET Identity 选项:

{
"IdentityOptions": {
"Password": {
"RequiredLength": 8,
"RequireDigit": true,
"RequireLowercase": true,
"RequireUppercase": false,
"RequireNonAlphanumeric": false
},
"Lockout": {
"MaxFailedAccessAttempts": 5,
"DefaultLockoutTimeSpan": "00:30:00"
}
}
}

注册与接入

// Program.cs —— 框架自动发现并启动 IdentityModuleStartup
builder.Services.AddFreeKitCore(); // 基础能力
// 聚合主宿主注册 IdentityModuleStartup(含上述 IdentityOptions、FreeSql、SignalR 等)
dotnet run --project src/Services/Host/FreeKit.Host
# Swagger: https://localhost:7000/kit_api/swagger

6. 开发示例

// 注入应用服务即可调用(示例:给当前用户授予某个权限)
public class MyService(IPermissionService permissionService, IUserService userService)
{
public async Task GrantAsync(Guid userId, string[] codes)
{
await permissionService.GrantPermissionAsync(new PermissionGrantReq
{
ProviderKey = userId.ToString(),
PermissionGrantType = PermissionGrantType.U, // 用户级
PermissionNames = codes
});
}
}
// 登录时判断设备是否需要二次验证(注入领域服务 IUserLoginDeviceManager)
public class LoginGuard(IUserLoginDeviceManager deviceManager)
{
public async Task<bool> NeedsExtraVerifyAsync(Guid userId, string userAgent)
{
var devices = await deviceManager.GetDevicesAsync(userId, userAgent);
var current = devices.FirstOrDefault(d => d.IsCurrentDevice);
return current is null || !current.IsTrustedAndValid;
}
}

相关文档