IAM
受众:开发者 摘要:统一身份 IAM:统一账号 / RBAC / 菜单权限 / 按钮权限 / 数据权限 / 审计 / 在线用户 / 外部身份。
IAM 是统一身份中心。当前项目不拆 admin_user 和 user 两套账号体系,而是使用统一账号、统一 token、统一权限和多身份扩展。
核心思想
统一账号主体:
iam_user不同登录方式、业务身份和扩展资料分别放到独立表:
iam_user_identity
iam_user_profile
iam_admin_profile
iam_merchant_profile
iam_developer_profile权限与组织模型:
iam_role
iam_permission
iam_menu
iam_user_role
iam_role_permission
iam_role_menu
iam_tenant
iam_organization
iam_department
iam_user_department
organization_post
iam_role_data_scope这样一个账号可以同时是普通用户、管理员、商户、开发者或运营人员,身份由角色、权限、profile 和数据范围共同表达。
用户关联键
所有用户相关数据关联只能使用稳定的用户主键:
user_id不要使用用户名、邮箱、手机号、钱包地址、OAuth openId 等可变登录标识作为业务关联键。
原因:
iam_user.id是唯一不可变账号身份。- username、email、mobile 可能会修改、解绑、换绑或禁用。
- OAuth、钱包、Telegram、Passkey、LDAP、SAML 等外部身份都属于登录身份扩展,不是账号主体。
- 审计、订单、通知、配置操作人、Web3 归集、支付流水等长期数据必须能在登录标识变化后继续准确关联用户。
推荐关系:
业务表.user_id -> iam_user.id
iam_user_identity.user_id -> iam_user.id
profile.user_id -> iam_user.idusername、email、mobile 只用于登录、检索、展示或通知投递,不作为跨表关系的事实来源。
为什么不拆账号表
不建议当前阶段拆成:
admin_user
user
merchant_user原因:
- 登录入口会重复。
- Token 生命周期会重复。
- 权限、审计、风控、多端登录都会重复。
- 后续 SSO、OAuth、钱包登录、Passkey、Telegram 等外部身份难以统一。
当前阶段统一使用 Sa-Token 的 StpUtil 主链路。只有未来出现完全独立安全域或完全独立业务系统时,才考虑多账号体系。
登录方式
当前支持:
| 方式 | 入口 | 说明 |
|---|---|---|
| 账号密码 | POST /api/v1/auth/login | 通过 iam_user_identity 查询 username/email/mobile |
| 短信验证码 | POST /api/v1/auth/login/sms | 先校验并消费验证码,再通过 mobile identity 登录 |
| 邮箱验证码 | POST /api/v1/auth/login/email | 先校验并消费验证码,再通过 email identity 登录 |
| 用户注册 | POST /api/v1/auth/register | 创建统一账号主体,并写入 username / email / mobile identity |
| 认证能力 | GET /api/v1/auth/capabilities | 返回注册、登录等前端需要的公开能力开关 |
| 找回密码 | POST /api/v1/auth/password/reset | 通过短信或邮箱验证码校验身份后重置密码 |
| 身份提供方 | GET /api/v1/auth/identity/providers | 查询当前已启用的 Identity Provider descriptor |
| 当前用户外部身份 | GET /api/v1/iam/users/me/identity-bindings | 查询当前用户已绑定的 OAuth、Telegram、钱包、微信、Passkey 等外部身份 |
| 身份提供方登录 | POST /api/v1/auth/identity/providers/{code}/login | 按 descriptor 的 flow 校验外部凭证载荷,校验通过后进入统一账号登录链路 |
| 身份提供方绑定 | POST /api/v1/auth/identity/providers/{code}/bind | 按 descriptor 的 flow 校验外部凭证载荷,校验通过后绑定到当前用户 |
| 身份提供方重认证 | POST /api/v1/auth/identity/providers/{code}/reauth | 按 descriptor 的 flow 校验外部凭证载荷,并确认该外部主体已属于当前用户 |
| OAuth | /api/v1/auth/oauth/{provider}/authorize、/api/v1/auth/oauth/{provider}/login | 第三方 provider 校验通过后进入统一账号 |
| OAuth2 授权服务器 | /oauth2/**、/api/v1/auth/oauth2/** | Sa-Token OAuth2 协议入口,默认关闭,用于给第三方系统接入本账号中心 |
| 钱包签名 | /api/v1/auth/wallet/challenge、/api/v1/auth/wallet/login | challenge 一次性短 TTL 缓存,验签通过后进入统一账号 |
短信验证码发送入口:
POST /api/v1/auth/verify/sms-codes
POST /api/v1/auth/verify/sms-codes/check
POST /api/v1/auth/login/sms认证类短信验证码发送和校验都会按手机号与 IP 组合限流,验证码哈希后写入 Cache,校验成功后立即消费,并通过 Notification 的 sms 通道发送。sms-codes 是 REST 资源集合命名,POST /sms-codes 一次仍然只创建并发送一个验证码。
短信登录直接调用 POST /api/v1/auth/login/sms,入参包含 mobile、code、scene、可选 countryCode / region 和可选 device。后端会先复用同一套验证码校验消费规则,再通过统一账号体系中的 mobile identity 登录并返回 LoginVO。登录不会回退直查 iam_user.mobile,避免绕过身份中心;如果历史用户缺少 mobile identity,应通过身份修复或绑定流程补齐。
手机号规范化已经接入服务端:mobile 字段会去除空格、横线、括号等常见分隔符,保留国际号码前导 +,并把 00 国际冠码转换为 +。服务端通过 libphonenumber 按默认地区 CN 解析并统一保存为 E.164,例如 13800138000 会保存为 +8613800138000。验证码缓存、限流、注册、找回密码、短信登录和绑定 / 换绑都会使用规范化后的手机号,避免 +86 138-0013-8000 与 0086 13800138000 写成两份 identity。认证短信发送 / 校验、短信登录、注册、找回密码和绑定手机号均可传入 countryCode / region,由服务端按用户选择地区合并解析。前端登录、注册、找回密码和绑定手机号页面应提供国家 / 地区选择器,默认地区可来自用户偏好、站点配置、Accept-Language 或设备环境。
如果 spring.open.iam.auth.login.verification.sms.enabled=false,短信验证码发送 / 校验资源不会注册,短信登录入口会返回“短信验证码未启用”的配置错误;账号密码、OAuth、Telegram 和钱包登录不受影响。
邮箱验证码入口:
POST /api/v1/auth/verify/email-codes
POST /api/v1/auth/verify/email-codes/check
POST /api/v1/auth/login/email认证类邮箱验证码与短信验证码遵循同一边界:按邮箱和 IP 组合限流,验证码哈希后写入 Cache,校验成功后立即消费,并通过 Notification 的 mail 通道发送。邮箱验证码登录不会回退直查 iam_user.email,只信任统一身份中心里的 email identity;如果历史用户缺少 email identity,应通过身份修复或绑定流程补齐。
自助注册入口:
POST /api/v1/auth/register注册会创建统一账号主体,并同步写入 username、email、mobile 三类本地 identity。email 会统一转为小写后保存,避免大小写导致登录查找不一致。第一阶段最小入参为 username、password、confirmPassword;nickname、email、mobile 可选。请求中如果带有 code,后端会根据 mobile 或 email 先校验并消费对应验证码;如果不带验证码,则只按账号、密码、邮箱、手机号等基础字段创建账号。
注册前会先检查统一身份中心中同类型 username、email、mobile 是否已存在,不回退直查 iam_user 字段。历史数据如果缺少 identity,应通过身份修复或绑定流程补齐。请求可选传入 source 和 device,用于认证审计里的注册来源和设备记录;未传 source 时默认按 password 记录。
账号密码注册由配置控制:
spring:
open:
auth:
register:
password:
enabled: true默认允许普通用户通过账号密码注册。数据库配置可以覆盖同名 key。关闭后,POST /api/v1/auth/register 会直接返回“账号密码注册暂未开放”,且不会先消费短信或邮箱验证码。
注册限流由 IAM 登录限流配置统一管理:
spring:
open:
iam:
auth:
login:
rate-limit:
register:
enabled: true
identifier-max-attempts: 3
identifier-window: 1m
ip-max-attempts: 10
ip-window: 1m注册限流同时按注册标识和 IP 生效,限流 key 会哈希处理,不把用户名、邮箱、手机号或 IP 明文写入 Redis。被限流时返回统一业务码 429,并写入注册失败审计。
前端视图不要本地硬编码注册开关,应先读取:
GET /api/v1/auth/capabilities响应中的 register.password.enabled 用于决定是否展示注册入口,以及提交前是否给出友好提示。响应中的 passwordPolicy 用于展示当前密码规则,默认 level=low、最少 6 位、最多 72 位、允许简单弱口令,并要求前端提供确认密码输入。
密码策略配置:
spring:
open:
iam:
password:
policy:
level: low
min-length: 6
max-length: 72
allow-weak: true
min-character-types: 1low 级别保持低门槛体验;medium / high / custom 可按站点安全要求增强。注册、后台创建用户、后台重置密码、当前用户修改密码、手机号 / 邮箱找回密码和安装向导创建首个管理员均复用同一套策略,避免前端和后端各自硬编码规则。
当前用户可以自助修改用户名:
PUT /api/v1/iam/users/me/username请求体:
{
"username": "new-name"
}修改用户名会同步更新统一身份中心里的 username identity,避免出现展示用户名和登录标识不一致。默认限制为 365 天内最多修改 3 次;提交相同用户名不会消耗次数。限额状态写在 iam_user 的用户名修改窗口字段中,公开能力接口会返回 usernameChangePolicy 供前端展示提示。
邮箱地址、有效手机号和合法 EVM 钱包地址格式都属于需要归属验证的受保护用户名:只有目标用户已经绑定同一邮箱、手机号或钱包地址时,当前用户改名或后台用户更新才允许保存;未绑定、绑定在其他用户下或只手动输入都会被拒绝。普通用户名、非法邮箱 / 手机号 / 钱包地址格式字符串以及未启用钱包身份提供方的运行环境不受钱包策略影响。钱包签名登录自动创建用户时,会优先使用已验签的钱包地址作为用户名;如果该地址用户名已经被占用,则回退到生成的外部身份用户名。手机号 / 邮箱登录或注册仍按默认用户名规则生成用户名,不强制用户名等于手机号或邮箱,避免误伤正常注册登录流程。
用户名修改策略配置:
spring:
open:
iam:
user:
username-change:
window-days: 365
max-count: 3找回密码入口:
POST /api/v1/auth/password/reset找回密码必须提供 mobile 或 email、code 和新密码。短信找回可同时传入 countryCode / region。后端先复用短信 / 邮箱验证码校验消费逻辑,再通过 IAM identity 找到用户并重置密码;不会回退直查 iam_user.mobile 或 iam_user.email。如果历史用户缺少对应 identity,应先通过身份修复或绑定流程补齐。
当前用户账号设置
当前登录用户账号设置统一放在 /api/v1/iam/users/me:
| API | 说明 |
|---|---|
PUT /api/v1/iam/users/me/password | 校验旧密码后修改当前用户密码 |
GET /api/v1/iam/users/me/bindings | 查询当前用户手机号和邮箱绑定状态 |
PUT /api/v1/iam/users/me/mobile | 校验短信验证码后绑定 / 换绑当前用户手机号 |
DELETE /api/v1/iam/users/me/mobile | 解绑当前用户手机号;如用户名等于该手机号,会自动切换为兜底用户名 |
PUT /api/v1/iam/users/me/email | 校验邮箱验证码后绑定 / 换绑当前用户邮箱 |
DELETE /api/v1/iam/users/me/email | 解绑当前用户邮箱;如用户名等于该邮箱,会自动切换为兜底用户名 |
POST /api/v1/iam/users/me/avatar | 上传当前用户头像,写入文件元数据并回写用户头像 URL |
GET /api/v1/iam/users/me/profile | 查询当前用户自己的普通用户扩展资料 |
PUT /api/v1/iam/users/me/profile | 保存当前用户自己的普通用户扩展资料,后端强制使用当前登录用户 ID |
GET /api/v1/iam/users/me/preferences | 查询当前用户账号偏好 |
PUT /api/v1/iam/users/me/preferences | 保存当前用户账号偏好 |
GET /api/v1/iam/users/me/roles | 查询当前用户自己的角色列表 |
GET /api/v1/iam/users/me/permissions | 查询当前用户自己的权限列表 |
GET /api/v1/iam/users/me/menus | 查询当前用户自己的菜单树 |
手机号和邮箱绑定变更继续复用认证验证码服务。请求中传入 mobile 或 email、code 和可选 scene,手机号绑定还可传入 countryCode / region。后端先校验并消费验证码,再写入 iam_user_identity。同一 identity 已属于当前用户时不会重复绑定;如果已属于其他用户,会返回 identity 已存在的业务错误。换绑会移除当前用户旧的同类型手机号 / 邮箱 identity,解绑会清空 iam_user.mobile / iam_user.email 展示字段;如果被移除的身份正好是当前用户名的归属来源,IAM 会自动把用户名切换为 user_<userId> 形式的兜底用户名并同步 username identity,防止已解绑邮箱、手机号或钱包地址继续被占用。后台身份管理的创建、更新和启停同样走这条用户服务链路,修改或停用邮箱 / 手机号 identity 时会同步展示字段并触发必要的用户名兜底;已失去归属的受保护 username 登录别名也会被清理。
当前用户 profile 接口只操作登录用户自己的 iam_user_profile,不要求 iam:profile:* 后台管理权限。普通用户端、PC 用户中心和移动端个人资料页面应优先使用 /users/me/** 自助接口,不要调用 /users/{userId}/roles、/iam/users/{userId}/profile 等后台管理接口。
当前用户偏好写入 iam_user_preference,按 user 分组保存键值,例如账号消息、系统消息、待办消息、营销消息、语言、时区和主题。偏好值为空时会禁用对应记录,不物理删除,避免唯一约束冲突。
头像上传默认使用 Storage 的 public disk,并在文件元数据中记录 user-avatar 业务类型。生产环境如需裁剪、压缩、尺寸校验或私有头像策略,应在 Storage / Image 处理能力完善后继续扩展。
公开用户资料
普通用户公开读取接口只返回可展示字段,不返回邮箱、手机号等敏感登录标识:
| API | 说明 |
|---|---|
GET /api/v1/iam/users/{id}/public-profile | 查询用户公开资料 |
GET /api/v1/iam/users/{id}/activities | 查询用户公开动态聚合出口 |
公开动态由 IAM 模块提供聚合出口,底层通过 core UserActivitySource 聚合可选业务来源。没有来源时返回真实空分页;当前 Blog 会贡献已发布文章,Forum 会贡献公开可见主题,CMS 会贡献当前用户创建的已发布内容,Mall 会贡献当前用户的已发布商品评价。响应返回稳定 id、type、displayType、title、summary、targetPath、url、occurredAt 和 createdAt 字段;新增业务来源继续按同一 contract 扩展,不需要让业务模块之间建立 Maven 强依赖。
联系人和用户关系
用户侧联系人、关注、粉丝、拉黑、好友申请和资料卡关系状态统一放在 IAM 用户域:
| API | 说明 |
|---|---|
GET /api/v1/iam/contacts | 查询当前用户好友联系人列表 |
GET /api/v1/iam/contacts/search | 搜索联系人候选 |
GET /api/v1/iam/contacts/summary | 查询联系人摘要 |
GET /api/v1/iam/contacts/following | 查询当前用户关注的联系人 |
GET /api/v1/iam/contacts/followers | 查询关注当前用户的联系人 |
GET /api/v1/iam/contacts/blocked | 查询当前用户拉黑的联系人 |
GET /api/v1/iam/contacts/friend-requests/sent | 查询当前用户发出的好友申请 |
GET /api/v1/iam/contacts/friend-requests/received | 查询当前用户收到的好友申请 |
POST /api/v1/iam/contacts/{id}/friend-request | 申请添加联系人 |
PUT /api/v1/iam/contacts/{id}/friend-request/accept | 通过联系人申请 |
PUT /api/v1/iam/contacts/{id}/friend-request/reject | 拒绝联系人申请 |
DELETE /api/v1/iam/contacts/{id}/friend | 删除联系人 |
PUT /api/v1/iam/contacts/{id}/follow | 关注联系人 |
DELETE /api/v1/iam/contacts/{id}/follow | 取消关注联系人 |
PUT /api/v1/iam/contacts/{id}/block | 拉黑联系人 |
DELETE /api/v1/iam/contacts/{id}/block | 解除拉黑联系人 |
PUT /api/v1/iam/contacts/{id}/preferences | 保存联系人偏好 |
POST /api/v1/iam/contacts/{id}/conversation | 从联系人或资料卡发起私聊 |
自助用户关系接口保留更贴近关系操作的入口:/api/v1/iam/user-relations/**;公开用户资料卡相关的关注接口也可通过 /api/v1/iam/users/{id}/follow 访问。新前端统一使用 IAM 域入口。
验证码风控开关
验证码风控默认关闭,不影响开发环境和基础登录体验。相关开关可以放在本地配置,也可以写入数据库配置:
spring:
open:
iam:
auth:
login:
captcha:
enabled: false
risk-based: true
sms-enabled: false
email-enabled: false
image-enabled: false
slider-enabled: false
rotate-enabled: false
drag-enabled: false设计边界:
enabled=false时不要求验证码。risk-based=true表示后续接入风控后只在风险命中时展示验证码。- 短信、邮箱、图形、滑动、旋转和拖动验证码是独立开关,可按项目需要逐步接入不同 Provider。
- 当前已落地短信验证码、邮箱验证码的发送、校验和登录;图形、滑动、旋转和拖动验证码已提供 challenge / check 接口,Admin / App / PC 登录页会根据
GET /api/v1/auth/capabilities返回的开关渲染真实可交互控件,先校验挑战通过后再提交原登录请求。控件使用普通请求 / 响应链路,不依赖 SSE,适合多端和受限运行环境。
登录成功后统一返回 JWT token。前后端分离交互使用:
Authorization: Bearer <token>不依赖 Cookie 和 HTTP Session。
账号密码登录默认接入通用限流底座:
spring:
open:
iam:
auth:
login:
rate-limit:
identifier-max-attempts: 5
identifier-window: 1m
ip-max-attempts: 20
ip-window: 1m规则:
- 按登录标识和 IP 同时限流。
- 登录标识和 IP 会在限流 key 中做哈希处理,避免敏感信息明文进入 Redis。
- 被限流时返回统一业务码
429,并写入登录失败审计。 - 多实例生产环境复用 Rate Limit starter 的 Redis store;lite profile 使用 memory store。
短信验证码默认限流策略:
spring:
open:
iam:
auth:
login:
verification:
sms:
mobile-max-attempts: 1
mobile-window: 1m
ip-max-attempts: 30
ip-window: 1h
verify-mobile-max-attempts: 5
verify-mobile-window: 5m
verify-ip-max-attempts: 60
verify-ip-window: 1h验证码短信发送会复用 Notification 的 sms 通道,因此后台短信发送日志和失败重试入口仍查看 Notification 发送日志。短信登录和后续手机号绑定都应复用同一 scene、缓存 key 和校验消费规则,不要另起一套验证码存储。
国际化手机号场景下,限流和缓存 key 必须使用服务端规范化后的手机号,不使用用户原始输入;日志和审计只能保存脱敏号码或 hash。SMS provider、厂商模板和签名由 SMS 路由按国家 / 地区、locale 和 scene 选择,IAM 只关心验证码是否发送、校验和消费成功。
首个超级管理员初始化
项目不创建固定默认管理员账号,也不提供默认测试密码。公开注册只能创建普通用户,不能自动创建超级管理员。
首次部署通过 安装向导 或 Install 模块 API 创建首个超级管理员。不再提供平行的公开初始化入口,避免同一职责在安装链路和系统链路中重复出现。
初始化入口由 Install 模块承担:
| 方法 | 路径 | 说明 |
|---|---|---|
POST | /api/v1/install/ + administrator | 创建或更新首个超级管理员 |
POST | /api/v1/install/execute | 执行完整安装流程,包含管理员创建和安装锁定 |
Install 会按安装令牌、安装状态、系统基础数据和 super-admin 角色绑定执行受控初始化;已安装或已有用户时按安装模块幂等语义处理,不另开平行初始化入口。
普通用户账号应通过自助注册、短信 / 邮箱验证码注册、OAuth / Telegram / 钱包等外部身份登录创建。不要在生产文档、部署脚本或种子数据中写死普通用户测试账号;联调需要账号时,先通过用户端注册或由管理员在后台受控创建。
Auth Facade
业务代码优先使用 Auth facade:
Long userId = CurrentUser.longId();
String loginId = Auth.idString();
Auth.checkLogin();
Auth.checkRole("admin");
Auth.checkPermission("iam:user:view");Auth 的登录主体 ID 不绑定固定 Java 类型。框架默认支持 String、常用数字类型和 UUID 的转换:
String loginId = Auth.idString();
Long legacyUserId = Auth.idLong();
UUID externalId = Auth.idAs(UUID.class);当前 iam_user.id 和多数业务表仍使用 BIGINT,所以存量业务可继续使用 CurrentUser.longId() / CurrentOperator.longId()。新能力如果需要字符串或 UUID 登录主体,应优先走 Auth.idString()、Auth.idAs(...) 或 CurrentUser.idAs(...),避免在业务代码里直接散落 Auth.id(Long.class)。
如需支持 ULID、Mongo ObjectId 等自定义 ID 类型,可注册 AuthIdConverter Bean;Auth starter 会把自定义 converter 注入 AuthIdConverterRegistry,默认 converter 继续作为兜底。
常用能力:
| 能力 | 说明 |
|---|---|
Auth.login(loginId) | 登录默认设备 |
Auth.login(loginId, device) | 登录指定设备 |
Auth.logout() | 当前 token 退出 |
Auth.id() / Auth.idAs(type) | 当前登录主体 ID |
Auth.idString() / Auth.idLong() | 当前登录主体 ID 的常用类型快捷入口 |
CurrentUser.* / CurrentOperator.* | 当前用户 / 当前操作人语义入口 |
Auth.token() | 当前 token |
Auth.sessions(loginId) | 指定账号多端会话 |
Auth.renewTimeout(...) | token 续期 |
Auth.checkRole(code) | 角色校验 |
Auth.checkPermission(code) | 权限校验 |
设备常量位于 AuthDevices:
default
web
pc
mobile
android
ios
mini_program
api用户管理
用户表 iam_user 是唯一账号主体。密码使用 BCrypt 哈希,接口 VO 不返回 password_hash。
主要接口:
| 方法 | 路径 | 说明 | 权限 |
|---|---|---|---|
GET | /api/v1/admin/iam/users | 分页查询用户 | iam:user:view |
POST | /api/v1/admin/iam/users | 创建用户 | iam:user:create |
GET | /api/v1/admin/iam/users/{id} | 查询用户详情 | iam:user:view |
PUT | /api/v1/admin/iam/users/{id} | 修改用户 | iam:user:update |
PUT | /api/v1/admin/iam/users/{id}/password | 重置用户密码 | iam:user:update |
POST | /api/v1/admin/iam/users/{id}/impersonation-sessions | 创建管理员代登录会话 | iam:user:impersonate |
GET | /api/v1/admin/iam/impersonation-sessions | 分页查询代登录会话 | iam:user:impersonate |
POST | /api/v1/admin/iam/impersonation-sessions/{id}/revocation | 撤销代登录会话 | iam:user:impersonate |
DELETE | /api/v1/admin/iam/users/{id} | 删除用户 | iam:user:delete |
DELETE | /api/v1/admin/iam/users | 批量删除用户 | iam:user:delete |
创建用户时会自动绑定可用的 username、email、mobile 身份。密码登录只查身份表,不直接回退查 iam_user.username。后台重置密码会重新写入 BCrypt 哈希,不返回密码明文。
后台用户管理支持管理员代登录目标用户。请求体必须填写 reason 和 timeoutSeconds,单位为秒;Admin 前端默认填写 3600 秒,也提供“永久”快捷项。有效期遵循认证层机制:timeoutSeconds <= 0 会统一归一为 -1 永久有效,正数表示有限秒数;Admin 输入框不单独拦截非正数,本模块不额外限制最大或最小时长。后端会校验后台权限、数据范围、目标用户启用状态和内置管理员保护规则,随后以内部 admin-impersonation 设备签发目标用户登录 token,并把代登录会话写入 iam_impersonation_session;永久会话的 expires_at 为空。代登录会记录登录审计和 token 审计,token 明文只在响应中返回一次,表内只保存 hash 与尾部短后缀。
代登录期间默认不内置固定操作黑名单,目标用户会话按目标用户自身角色、权限和数据范围执行。生产环境如需禁止提现、支付密钥、绑定身份等高风险操作,应在后续可配置策略中基于代登录会话上下文做统一拦截,而不是在代登录签发流程写死限制。
代登录会话支持运维闭环:列表接口可按操作人、目标用户和状态(active 在途 / expired 已过期未撤销 / ended 已撤销)过滤,active 但已超过 expires_at 的会话在查询结果中展示为 expired,expires_at 为空的永久会话保持 active,不额外落库。撤销接口会按目标用户与代登录设备注销 token(token 表内只存哈希,无法按值注销;同一用户同设备的并发代登录会话会一并失效)、把会话状态置为 ended 并记录 impersonate_revoke token 审计;撤销前同样校验数据范围。后台用户列表行操作的"代登录会话"入口可查看并撤销目标用户的会话。
代登录对 App / PC 等普通用户前端保持透明:用户侧 /api/v1/auth/sessions 不返回内部代登录会话,用户侧按设备退出 / 踢下线接口也不能操作内部代登录设备;代登录状态、操作人、目标用户、审计记录、退出入口和风险控制只在后端与 Admin 管理端处理。
身份绑定
身份表:
iam_user_identity内置身份类型:
| 类型 | 说明 |
|---|---|
username | 用户名 |
email | 邮箱 |
mobile | 手机号 |
oauth | 第三方 OAuth |
telegram | Telegram Login Widget |
wallet | 钱包地址 |
wechat-official-openid / wechat-miniapp-openid / wechat-unionid | 微信外部身份 |
passkey | Passkey 凭证 |
ldap | LDAP / AD 企业目录身份 |
saml | SAML 企业 SSO 身份 |
主要接口:
| 方法 | 路径 | 说明 | 权限 |
|---|---|---|---|
GET | /api/v1/iam/identities | 分页查询身份绑定 | iam:identity:view |
GET | /api/v1/iam/identities/{id} | 查询身份详情 | iam:identity:view |
POST | /api/v1/iam/identities | 创建身份绑定 | iam:identity:create |
PUT | /api/v1/iam/identities/{id} | 修改身份绑定 | iam:identity:update |
PUT | /api/v1/iam/identities/{id}/enabled | 启用或停用身份绑定 | iam:identity:update |
PUT | /api/v1/iam/identities/{id}/verified | 标记身份绑定已验证 | iam:identity:update |
DELETE | /api/v1/iam/identities/{id} | 删除身份绑定 | iam:identity:delete |
DELETE | /api/v1/iam/identities | 批量删除身份绑定 | iam:identity:delete |
身份禁用后不能继续用于登录;后台可以单独启停身份绑定,也可以将已线下核验的身份标记为已验证。
当前用户账号中心的外部身份绑定接口:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/iam/users/me/identity-bindings | 只返回当前用户已绑定的外部身份,不包含 username / email / mobile 等凭证身份 |
DELETE | /api/v1/iam/users/me/identity-bindings/{id} | 解绑当前用户自己的外部身份;如果是他人身份、凭证身份或不存在的绑定,统一按身份不存在处理 |
POST | /api/v1/auth/identity/providers/{code}/bind | 复用对应 provider 的 verify() 步骤,把已验证外部主体绑定到当前用户;主体已属于其他用户时返回身份已存在 |
POST | /api/v1/auth/identity/providers/{code}/reauth | 复用对应 provider 的 verify() 步骤,确认已验证外部主体已绑定到当前用户;不创建用户、不新增绑定 |
解绑会保留至少一个登录身份,避免用户把最后一个可登录身份删掉。绑定和重认证只接收 flow 对应的原始凭证载荷,例如 OAuth 的 code/state/redirectUri、Telegram 的 signed payload、钱包的 challengeId/signature、微信公众号的 code/lang 或小程序的 code。如果解绑的是当前用户名归属的钱包地址,IAM 会自动切换为兜底用户名并同步 username identity;OAuth、Telegram、微信、Passkey、企业 SSO 等外部身份继续通过同一绑定中心支持绑定、重认证和解绑。
钱包地址也可以作为用户名展示,但前提是该地址已经作为当前用户的 wallet 身份绑定并通过后端验签确认归属。邮箱和手机号也可以作为用户名展示,但前提是该邮箱或手机号已经作为目标用户已验证 identity 绑定。后台或用户侧不能把用户名改成未绑定、已归属他人或已解绑的邮箱、手机号或钱包地址;钱包登录自动建号只会使用本次验签通过的同一地址作为候选用户名,地址已被其他用户占用时会生成 wallet_<provider>_* 形式的用户名。
当前内置 Identity Provider discovery 包含:
| Provider code | Flow | Protocol | 说明 |
|---|---|---|---|
OAuth provider code,例如 github / oidc / wechat-mp | authorization-code | oauth2 / oidc | 使用 /api/v1/auth/oauth/{provider}/authorize 与 /login |
telegram | signed-payload | telegram-login-widget | 使用 /api/v1/auth/identity/providers/telegram/login 校验 Telegram Login Widget signed payload |
evm-wallet | challenge-signature | wallet-signature | 钱包签名消息 + signature |
passkey | challenge-signature | webauthn | Auth Passkey 负责 WebAuthn 注册、凭据持久化、assertion 验签和登录;descriptor 暴露注册 / 登录路径 |
wechat-official:<accountCode> | authorization-code | oauth2 | core-wechat 中启用的公众号账号;metadata 带 identityType=wechat-official-openid 和 identityProvider=<accountCode 小写> |
wechat-miniapp:<accountCode> | miniapp-code | wechat-miniapp | core-wechat 中启用的小程序账号;metadata 带 identityType=wechat-miniapp-openid 和 identityProvider=<accountCode 小写> |
企业目录 provider code,例如 corp-ldap | ldap-password | ldap / ad | 使用 /api/v1/auth/identity/providers/{code}/login 校验目录用户名和密码 |
企业 SAML provider code,例如 corp-saml | saml-assertion | saml2 | 使用 /api/v1/auth/identity/providers/{code}/login 校验 SAMLResponse / Assertion |
企业 OIDC provider code,例如 corp-oidc | authorization-code | oidc | 复用 /api/v1/auth/oauth/{provider}/authorize 与 /login,支持自定义 issuer / 端点 |
微信 provider code 中的 accountCode 来自 wechat_account.account_code 原值,用于微信服务查找账号;descriptor metadata 中的 identityProvider 使用小写账号编码,用于 IAM 身份表的 openid provider 查询。统一绑定中心会从 provider code 解析账号编码,忽略 payload 中可被篡改的 accountCode;后台绑定数量和绑定列表会优先使用 descriptor metadata 中的 identityType / identityProvider,因此微信多账号会按真实 openid 绑定范围查询。微信专用 /api/v1/auth/wechat-*/bind 仍保留完整 openid + unionid 绑定行为;通用 /identity/providers/{code}/bind 只绑定本次 verify 得到的主 openid subject。
Passkey provider 的 discovery descriptor 只告诉前端可用入口,不在 registry 层做 WebAuthn 校验。真实链路归属 Auth Passkey:
| 方法 | 路径 | 说明 |
|---|---|---|
POST | /api/v1/auth/passkey/registration-options | 当前登录用户生成 WebAuthn 注册 challenge,返回 RP、用户句柄、算法参数、exclude credentials 和注册提交路径 |
POST | /api/v1/auth/passkey/register | 当前登录用户提交 attestation,校验 challenge / origin / rpId / authenticator data,持久化 credential public key 并绑定 passkey 身份 |
POST | /api/v1/auth/passkey/login-options | 公开登录入口生成 assertion challenge;可按用户名返回 allow credentials,匿名场景可让浏览器选择 resident credential |
POST | /api/v1/auth/passkey/login | 提交 assertion,校验 challenge / origin / rpId / signature / signCount 后进入统一登录、风控、审计和 Token 链路 |
凭据数据落在 iam_passkey_credential,包含 credential id hash、COSE public key、算法、AAGUID、signCount、RP ID、origin、transports 和最后使用时间。注册成功后会同步写入 iam_user_identity,身份类型为 passkey,provider 固定为 passkey,identifier 使用 credential id hash,便于后台身份绑定、解绑保护和登录审计统一消费。当前实现支持 ES256 和 RS256 公钥验签;attestation 证书链不作为准入条件,生产如需硬件根证书白名单,可以在 Auth Passkey 层继续补充策略,不应放到 Identity Provider registry。
企业目录 provider 通过 spring.open.iam.providers.<code> 配置启用。LDAP / AD adapter 只做目录凭据校验,不处理登录状态、用户创建、绑定或审计;校验成功后统一收敛为 identityType=ldap、provider=<code>、identifier=<目录稳定属性>,再由 IAM 登录链路处理风控、审计、Token 和可选自动创建用户。
spring:
open:
iam:
providers:
corp-ldap:
enabled: true
provider: Corporate Directory
flow: ldap-password
protocol: ldap
auto-create-user: true
ldap:
url: ldap://127.0.0.1:389
manager-dn: cn=manager,dc=example,dc=com
manager-password: ${LDAP_MANAGER_PASSWORD}
base-dn: ou=People,dc=example,dc=com
user-search-filter: "(uid={username})"
identifier-attribute: uid
nickname-attribute: cn
email-attribute: mail
mobile-attribute: mobile
connect-timeout: 5s
read-timeout: 5s如果不配置 manager-dn,可改用 user-dn-pattern 直接按用户 DN bind:
spring:
open:
iam:
providers:
corp-ad:
enabled: true
provider: Corporate AD
flow: ldap-password
protocol: ad
ldap:
url: ldap://ad.example.com:389
user-dn-pattern: "{username}@example.com"
identifier-attribute: sAMAccountName
nickname-attribute: displayName
email-attribute: mailPC / App 登录页会读取 provider descriptor;当 flow=ldap-password 时展示目录用户名 / 密码表单,并向 descriptor 的 loginPath 提交:
{
"payload": {
"username": "alice",
"password": "secret"
},
"device": "pc",
"createUserIfAbsent": true
}SAML provider 同样通过 spring.open.iam.providers.<code> 配置启用。SAML adapter 只做 issuer、audience、时间窗口、XML Signature 和属性映射校验;校验成功后统一收敛为 identityType=saml、provider=<code>、identifier=<NameID 或配置属性>。默认要求 Assertion 自身带有效 XML Signature,并使用配置的 IdP X.509 证书验签。开发或本地联调可临时关闭 require-signed-assertion,生产环境不建议关闭。
spring:
open:
iam:
providers:
corp-saml:
enabled: true
provider: Corporate SAML
flow: saml-assertion
protocol: saml2
auto-create-user: true
saml:
issuer: https://idp.example.com/saml
audience: spring-open
certificate: ${SAML_IDP_CERTIFICATE}
require-signed-assertion: true
clock-skew: 2m
assertion-field: samlResponse
identifier-attribute: employeeNumber
nickname-attribute: displayName
email-attribute: email
mobile-attribute: mobileSAML 登录由 IdP 或前置网关拿到 SAMLResponse 后回传平台的通用外部登录入口:
{
"payload": {
"samlResponse": "<Base64 SAMLResponse>"
},
"device": "pc",
"createUserIfAbsent": true
}企业 OIDC 不需要单独新建 adapter;它仍是 authorization-code flow,只是在配置中把自定义 provider 声明为 protocol=oidc。后端会把 corp-oidc 这类别名路由到通用 OIDC provider,并读取 spring.open.iam.providers.corp-oidc.* 的 issuer、端点、client 和字段映射。
spring:
open:
iam:
providers:
corp-oidc:
enabled: true
provider: Corporate OIDC
flow: authorization-code
protocol: oidc
client-id: ${OIDC_CLIENT_ID}
client-secret: ${OIDC_CLIENT_SECRET}
redirect-uri: https://app.example.com/login/oauth-callback
scope: openid profile email
properties:
issuer-uri: https://idp.example.com
identifier-field: sub
nickname-field: name
email-field: email
pkce: trueSAML / LDAP / 企业 OIDC 都只负责“外部凭证校验为已验证主体”。登录、绑定、重认证、自动创建用户、风控、审计和 Token 签发仍复用 IAM 通用链路,不在 adapter 中重复实现业务流程。
后台身份提供方运行时配置台归属 IAM 管理面:
| 方法 | 路径 | 说明 | 权限 |
|---|---|---|---|
GET | /api/v1/admin/iam/identity-providers | 分页查询当前可发现的 provider descriptor、运行时配置和密钥引用摘要 | iam:identity-provider:view |
GET | /api/v1/admin/iam/identity-providers/{code} | 查询 provider 详情 | iam:identity-provider:view |
PUT | /api/v1/admin/iam/identity-providers/{code} | 保存展示名、排序、启停状态、公开元数据和备注 | iam:identity-provider:update |
PUT | /api/v1/admin/iam/identity-providers/{code}/status | 启用或停用 provider | iam:identity-provider:update |
POST | /api/v1/admin/iam/identity-providers/{code}/test | 执行本地 descriptor 可用性测试并记录测试状态 | iam:identity-provider:test |
POST | /api/v1/admin/iam/identity-providers/{code}/secrets/rotate | 轮换 secret 引用、提示、摘要和版本 | iam:identity-provider:secret:rotate |
GET | /api/v1/admin/iam/identity-providers/{code}/bindings | 查询指定 provider 的外部身份绑定 | iam:identity-provider:view |
运行时配置写入 iam_identity_provider_config,只覆盖公开发现和绑定入口需要的启停、排序、展示名、公开 metadata 与测试结果。密钥引用写入 iam_identity_provider_secret,只保存 secretRef、secretHint、摘要和版本;请求和响应都不承载明文 secret,返回给前端的引用会做掩码。停用 provider 后,GET /api/v1/auth/identity/providers 的公开发现结果、绑定和重认证都会同步拒绝该 provider。
外部身份登录支持 LoginRiskGuard 扩展点。平台会在外部身份 verify 完成、签发会话前依次调用已注册的 guard;没有 guard 时等同放行,不产生额外开销。guard 可以基于用户、登录类型、provider、外部主体、设备、IP 和是否本次自动创建用户返回拒绝决策;拒绝后不会签发会话,并会把失败原因写入登录审计。
Profile
Profile 用于承载不同业务身份的扩展资料。
| Profile | 表 | 说明 |
|---|---|---|
| 普通用户 | iam_user_profile | 普通用户资料 |
| 管理员 | iam_admin_profile | 管理员资料 |
| 商户 | iam_merchant_profile | 商户资料 |
| 开发者 | iam_developer_profile | 开发者资料 |
Profile 支持两类接口:
- 资源型接口:按 profile ID 分页、详情、新增、修改、删除、批量删除。
- 用户路径接口:按
userId查询或保存对应 profile,并支持启停。
示例:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/admin/iam/user-profiles | 分页查询普通用户 profile |
PUT | /api/v1/admin/iam/users/{userId}/profile | 保存普通用户 profile |
GET | /api/v1/admin/iam/admin-profiles | 分页查询管理员 profile |
PUT | /api/v1/admin/iam/admins/{userId}/profile/enabled | 启停管理员 profile |
GET | /api/v1/admin/iam/merchant-profiles | 分页查询商户 profile |
GET | /api/v1/admin/iam/developer-profiles | 分页查询开发者 profile |
权限前缀:
iam:profile:*Profile 查询和 ID 型接口按目标用户接入数据范围校验。
RBAC
RBAC 使用角色、权限和菜单三类资源。
| 表 | 说明 |
|---|---|
iam_role | 角色 |
iam_permission | 权限 |
iam_menu | 菜单 |
iam_user_role | 用户角色关系 |
iam_role_permission | 角色权限关系 |
iam_role_menu | 角色菜单关系 |
内置角色编码:
super-admin
admin
operator
merchant
developer
usersuper-admin 在迁移初始化时绑定内置权限、菜单和 ALL 数据范围。admin 和 operator 作为第一阶段后台模板角色,会默认绑定一组低风险后台菜单、权限和数据范围:
admin:用户、角色、权限、菜单、配置、字典、模板、文件、通知、会话、认证审计、在线用户和操作日志等常规后台管理入口。operator:通知消息、发送日志、会话运维、内容安全审核和操作日志等运营 / 客服入口。
数据范围第一阶段采用保守模板:admin 默认 ALL,用于常规后台管理;operator 默认 SELF,只访问本人处理或被显式分配的数据。默认模板不会授予 SQL 控制台、安装执行、Web3 资金执行、危险删除等高风险权限。developer 已由 Open Platform 模块声明开放平台后台只读模板,覆盖应用、套餐、Scope、路由 Scope、调用日志、用量事件、账务和 AI 运营只读入口;merchant 已由 Mall 模块声明商城后台只读模板,覆盖商品、订单、售后、优惠券、履约、支付 / 交易 / 库存观测入口,不授予发货、退款、作废、删除等写操作;user 暂不绑定后台菜单和默认数据范围,普通用户默认不进入后台。
Admin 角色管理页会展示内置角色模板策略,包含权限策略、菜单策略、默认数据范围和注意事项。内置角色编码由迁移保留,后端会拒绝手动创建、改码和删除;super-admin 禁止停用,菜单权限、API 权限和数据范围不能在角色管理页保存变更。前端会禁用或提示这些危险操作,服务层校验仍是最终准入。
权限标识使用稳定字符串:
iam:user:view
iam:user:create
iam:user:update
iam:user:delete业务代码引用内置权限时优先使用 PermissionCodes,不要散落手写字符串。
角色、权限、菜单接口
角色:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/admin/iam/roles | 分页查询角色 |
POST | /api/v1/admin/iam/roles | 创建角色 |
GET | /api/v1/admin/iam/roles/{id} | 查询角色详情 |
PUT | /api/v1/admin/iam/roles/{id} | 修改角色 |
DELETE | /api/v1/admin/iam/roles/{id} | 删除角色 |
DELETE | /api/v1/admin/iam/roles | 批量删除角色 |
GET | /api/v1/admin/iam/roles/{roleId}/permissions | 查询角色权限 |
PUT | /api/v1/admin/iam/roles/{roleId}/permissions | 分配角色权限 |
GET | /api/v1/admin/iam/roles/{roleId}/menus | 查询角色菜单 |
PUT | /api/v1/admin/iam/roles/{roleId}/menus | 分配角色菜单 |
权限:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/admin/iam/permissions | 分页查询权限 |
POST | /api/v1/admin/iam/permissions | 创建权限 |
GET | /api/v1/admin/iam/permissions/{id} | 查询权限详情 |
PUT | /api/v1/admin/iam/permissions/{id} | 修改权限 |
DELETE | /api/v1/admin/iam/permissions/{id} | 删除权限 |
DELETE | /api/v1/admin/iam/permissions | 批量删除权限 |
菜单:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/admin/iam/menus | 查询菜单列表 |
GET | /api/v1/admin/iam/menus/tree | 查询菜单树 |
POST | /api/v1/admin/iam/menus | 创建菜单 |
GET | /api/v1/admin/iam/menus/{id} | 查询菜单详情 |
PUT | /api/v1/admin/iam/menus/{id} | 修改菜单 |
DELETE | /api/v1/admin/iam/menus/{id} | 删除菜单 |
DELETE | /api/v1/admin/iam/menus | 批量删除菜单 |
菜单支持目录、菜单、按钮三种类型。菜单用于前端导航和展示,权限用于后端鉴权,两者通过权限标识保持弱关联。
用户授权
用户角色、权限和菜单接口:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/admin/iam/users/{userId}/roles | 查询用户角色 |
PUT | /api/v1/admin/iam/users/{userId}/roles | 分配用户角色 |
GET | /api/v1/admin/iam/users/{userId}/permissions | 查询用户权限 |
GET | /api/v1/admin/iam/users/{userId}/menus | 查询用户菜单树 |
GET | /api/v1/organization/users/{userId}/posts | 查询用户岗位 |
PUT | /api/v1/organization/users/{userId}/posts | 分配用户岗位 |
关系表通常通过聚合接口管理,不开放裸关系表 CRUD。
租户、组织、部门、岗位
组织模型用于数据权限和后台管理。
| 资源 | 路径 | 说明 |
|---|---|---|
| 租户 | /api/v1/iam/tenants | 租户管理 |
| 组织 | /api/v1/iam/organizations | 组织管理,归属租户 |
| 部门 | /api/v1/iam/departments | 部门管理,归属租户和组织 |
| 岗位 | /api/v1/organization/posts | 岗位管理,归属租户和组织 |
| 用户租户归属 | /api/v1/iam/user-tenants | 用户多租户关系 |
| 用户组织归属 | /api/v1/iam/user-organizations | 用户多组织关系 |
| 用户部门归属 | /api/v1/iam/user-departments | 用户多部门关系 |
这些资源均提供标准后台接口:
GET /resources
GET /resources/{id}
POST /resources
PUT /resources/{id}
DELETE /resources/{id}
DELETE /resources批量删除 ID 通过请求体传递,不放到 URL 中。
工作空间
工作空间由请求头解析:
X-Tenant-Id: 1
X-Organization-Id: 10
X-Department-Id: 100当前工作空间接口:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/iam/workspace/current | 查询当前工作空间,并返回当前成员、席位占用、角色和权限快照 |
工作空间不固定写入 token。每次请求按当前用户归属关系校验请求头中的租户、组织和部门。currentMember / seat / roleCodes / permissionCodes 只表示当前登录用户在当前工作空间下的可用上下文;成员列表仍通过 GET /api/v1/iam/workspace/members 分页查询。
数据权限
数据权限由角色数据范围控制:
iam_role_data_scope内置角色模板的数据范围默认值:
| 角色 | 默认数据范围 | 说明 |
|---|---|---|
super-admin | ALL | 最高权限角色,迁移预置全量数据范围 |
admin | ALL | 常规后台管理员,默认可访问平台后台管理数据 |
operator | SELF | 运营 / 客服角色,默认只访问本人处理或被显式分配的数据 |
merchant | 暂不绑定 | Mall 已声明后台只读菜单和权限;不绑定默认数据范围,商户写操作仍需商户 / 店铺 ownership 和数据范围强校验 |
developer | 暂不绑定 | Open Platform 已声明后台只读菜单和权限;不绑定默认数据范围,开发者写操作仍需 workspace / 应用归属强校验 |
user | 暂不绑定 | 普通用户默认不进入后台,用户端数据按当前登录用户校验 |
支持范围:
| 类型 | 说明 |
|---|---|
ALL | 全部数据 |
TENANT | 当前租户 |
ORGANIZATION | 当前组织 |
ORGANIZATION_AND_CHILDREN | 当前组织及下级组织 |
DEPARTMENT | 当前部门 |
DEPARTMENT_AND_CHILDREN | 当前部门及下级部门 |
SELF | 本人数据 |
CUSTOM | 自定义租户、组织、部门 |
角色数据范围接口:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/iam/role-data-scopes | 分页查询角色数据范围 |
GET | /api/v1/iam/role-data-scopes/{id} | 查询详情 |
POST | /api/v1/iam/role-data-scopes | 创建数据范围 |
PUT | /api/v1/iam/role-data-scopes/{id} | 修改数据范围 |
DELETE | /api/v1/iam/role-data-scopes/{id} | 删除数据范围 |
DELETE | /api/v1/iam/role-data-scopes | 批量删除数据范围 |
super-admin 的 ALL 数据范围是安装基线的一部分,角色数据范围接口会拒绝在角色管理页新增、修改或删除该角色的数据范围。
当前采用显式数据范围管理器:
DataScopeManager
DataScopeQuerySupport
DataScopeAccessSupport
UserDataScopeQuerySupport没有使用全局 SQL 拦截器。这样更清晰,也更容易审计每个后台接口到底按哪些字段做范围限制。
认证审计
登录日志:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/iam/auth/audit/login-logs | 分页查询登录日志 |
GET | /api/v1/iam/auth/audit/risk-summary | 查询最近登录风控摘要 |
GET | /api/v1/iam/auth/audit/login-logs/{id} | 查询登录日志详情 |
DELETE | /api/v1/iam/auth/audit/login-logs/{id} | 删除登录日志 |
DELETE | /api/v1/iam/auth/audit/login-logs | 批量删除登录日志 |
外部身份 LoginRiskGuard 拒绝登录时会写入 success=false 的登录日志,failureReason 默认使用 {messages.auth.login.risk-rejected},也可以由 guard 返回业务自定义 message key。后台 /iam/login-logs 页面和 risk-summary 摘要会消费这些失败记录,不需要额外创建风控表。
Token 审计:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/iam/auth/audit/token-logs | 分页查询 Token 审计日志 |
GET | /api/v1/iam/auth/audit/token-logs/{id} | 查询 Token 审计详情 |
DELETE | /api/v1/iam/auth/audit/token-logs/{id} | 删除 Token 审计日志 |
DELETE | /api/v1/iam/auth/audit/token-logs | 批量删除 Token 审计日志 |
Token 审计只保存 token SHA-256 hash 和尾部短后缀,不保存 token 明文。
在线会话
当前用户会话:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/auth/sessions | 查询当前用户会话 |
POST | /api/v1/auth/sessions/{device}/logout | 当前用户按设备退出 |
POST | /api/v1/auth/sessions/{device}/kickout | 当前用户按设备踢下线 |
后台在线会话:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/admin/auth/online-sessions | 分页查询在线会话 |
POST | /api/v1/admin/auth/online-sessions/{userId}/devices/{device}/kickout | 后台踢下线用户设备 |
接口不返回 token 明文。
不同 Sa-Token JWT 模式对多端会话的能力边界不同。项目默认使用 simple,确保后台在线会话强退、当前用户按设备退出 / 踢下线和 Token 审计可用。mixin、stateless 更适合特殊轻状态场景,可能禁用按 loginId / token 生命周期操作;如果主动切换这些模式,前端应隐藏对应按钮或展示“不支持当前操作”。
操作日志
Audit 会自动记录 /api/v1/admin/** 下的写请求:
POST
PUT
PATCH
DELETE操作日志接口:
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/v1/admin/audit/operation-logs | 分页查询操作日志 |
GET | /api/v1/admin/audit/operation-logs/{id} | 查询操作日志详情 |
DELETE | /api/v1/admin/audit/operation-logs/{id} | 删除操作日志 |
DELETE | /api/v1/admin/audit/operation-logs | 批量删除操作日志 |
操作日志记录 URI、查询参数、路径变量、operationId、状态、traceId、来源 IP、User-Agent 等信息,不采集请求 body,避免敏感数据泄露。
配置参数
IAM 身份提供方统一使用 spring.open.iam.providers.*。GitHub、Google、OIDC、支付宝、微博、百度、抖音、Gitee、QQ、微信、钉钉、飞书和企业微信属于 OAuth/OIDC provider;Telegram 属于 Login Widget provider;EVM Wallet 属于钱包签名 provider。配置层只表达“身份提供方”,OAuth、签名、hash 校验等协议细节由对应 provider 内部处理。
登录页可以先调用:
GET /api/v1/auth/identity/providers该接口只返回当前已启用的身份提供方入口,例如 OAuth / 企业 OIDC provider 的 authorizePath / loginPath、Telegram 的 loginPath 和 Login Widget 公开元数据、钱包的 challengePath / loginPath、Passkey 的 registrationOptionsPath / registrationPath / challengePath / loginPath、LDAP / SAML 的通用 loginPath,以及 core-wechat 中启用公众号 / 小程序账号产生的微信 descriptor。前端不需要硬编码哪些 provider 已启用;账号密码登录仍按业务页面自身决定是否展示。Telegram 的 botUsername、buttonSize、cornerRadius、requestAccess 可以返回给前端,bot-token 只用于后端校验 hash,不会出现在响应中;SAML 只返回字段名、是否要求签名和是否配置证书等公开 metadata,不返回证书或密钥。
SSO 边界
当前 IAM 已经可以作为 OAuth / OIDC Client 接入外部身份中心,例如 GitHub、Google、企业 OIDC、钉钉、飞书、企业微信、Gitee、QQ、微信和 Telegram;也可以通过 ldap-password 和 saml-assertion flow 接入企业目录和 SAML 身份中心。接入后仍然会落到统一账号、统一 token、统一 RBAC、统一审计和统一数据范围链路。
Auth starter 同时提供 Sa-Token OAuth2 Authorization Server 的框架底座,默认关闭。开启后,本项目可以作为授权服务器向第三方系统签发 token;这条链路和上面的“接入外部 OAuth/OIDC provider 登录”不是同一个方向。当前底座提供协议端点、客户端配置、授权模式开关、scope 分级、token 生命周期配置、后台客户端管理和 OIDC 发现元数据。授权确认仍复用 Sa-Token 协议确认端点,客户端可在后台设置是否自动确认 scope;JWKS、ID Token 非对称签名和多租户隔离策略可在此底座上继续扩展。
OAuth2 客户端支持两类来源:
- 静态配置:
spring.open.auth.oauth2.clients.<client>.*,适合部署期固定客户端。 - 后台维护:IAM 菜单“OAuth2 客户端”,接口前缀
/api/v1/admin/iam/oauth2-clients,权限为iam:oauth2-client:*,数据落表iam_oauth2_client。启用状态的数据库客户端会通过 Auth starter provider 合并进 Sa-Token OAuth2 server config;同 clientId 时以后台维护的启用客户端为准。
Sa-Token OAuth2 协议端点同时支持标准根路径和平台版本化路径:
| 能力 | 标准路径 | 平台版本化路径 |
|---|---|---|
| 授权 | /oauth2/authorize | /api/v1/auth/oauth2/authorize |
| 换取令牌 | /oauth2/token | /api/v1/auth/oauth2/token |
| 刷新令牌 | /oauth2/refresh | /api/v1/auth/oauth2/refresh |
| 撤销令牌 | /oauth2/revoke | /api/v1/auth/oauth2/revoke |
| 客户端令牌 | /oauth2/client_token | /api/v1/auth/oauth2/client_token |
| 授权页登录 | /oauth2/doLogin | /api/v1/auth/oauth2/doLogin |
| 授权确认 | /oauth2/doConfirm | /api/v1/auth/oauth2/doConfirm |
OIDC 发现元数据同时支持:
/.well-known/openid-configuration/api/v1/auth/oauth2/.well-known/openid-configuration
| 配置 | 默认值 | 说明 |
|---|---|---|
spring.open.auth.enabled | true | 是否启用 Auth starter |
spring.open.auth.default-login-device | default | 默认登录设备 |
spring.open.auth.persistence.store | redis | 应用默认登录态存储,支持 memory、redis |
spring.open.auth.persistence.key-prefix | auth:satoken | Sa-Token Redis key 前缀 |
spring.open.auth.jwt.enabled | true | 是否启用 JWT StpLogic |
spring.open.auth.jwt.mode | simple | JWT 模式:simple、mixin、stateless;默认使用 simple,保留 token 映射和终端会话能力,支持后台强退、按设备下线和 Token 审计 |
sa-token.jwt-secret-key | ${SPRING_OPEN_AUTH_JWT_SECRET_KEY:${spring.application.secret:}} | JWT 签名密钥统一入口;未配置专用密钥时使用应用共享密钥,数据库配置中心播种的共享密钥可覆盖派生默认值(见下方读取顺序) |
spring.open.auth.oauth2.enabled | false | 是否启用 Sa-Token OAuth2 授权服务器 |
spring.open.auth.oauth2.authorization-code-enabled | true | 是否启用授权码模式 |
spring.open.auth.oauth2.implicit-enabled | false | 是否启用隐式模式 |
spring.open.auth.oauth2.password-enabled | false | 是否启用密码模式 |
spring.open.auth.oauth2.client-credentials-enabled | true | 是否启用客户端凭证模式 |
spring.open.auth.oauth2.clients.<client>.client-secret | 空 | OAuth2 客户端密钥;<client> 可作为 clientId 兜底 |
spring.open.auth.oauth2.clients.<client>.redirect-uris | 空 | 允许的 OAuth2 回调地址列表 |
spring.open.auth.oauth2.clients.<client>.scopes | openid,profile | 客户端允许申请的 scope |
spring.open.auth.oauth2.clients.<client>.grant-types | authorization_code,refresh_token | 客户端允许使用的授权模式 |
spring.open.auth.multi-device.concurrent | true | 是否允许同账号多端同时在线 |
spring.open.auth.multi-device.share-token | true | 多端是否共享 token |
spring.open.auth.multi-device.max-login-count | 12 | 同账号最大登录数量,-1 表示不限 |
spring.open.iam.user.enabled | true | 是否启用 IAM 用户能力 |
spring.open.iam.enabled | true | 是否启用 IAM 身份和 profile 能力 |
spring.open.iam.rbac.enabled | true | 是否启用 RBAC |
spring.open.iam.tenant.enabled | true | 是否启用租户、组织、部门归属能力 |
spring.open.iam.data-scope.enabled | true | 是否启用数据权限 |
spring.open.iam.workspace.enabled | true | 是否启用工作空间解析 |
spring.open.iam.auth.audit.enabled | true | 是否启用认证审计 |
spring.open.audit.operation.enabled | true | 是否启用操作日志 |
spring.open.iam.auth.session.online.enabled | true | 是否启用在线会话后台管理 |
spring.open.iam.auth.login.rate-limit.enabled | true | 是否启用登录限流配置 |
spring.open.iam.auth.login.rate-limit.password.enabled | true | 是否启用账号密码登录限流 |
spring.open.iam.auth.login.rate-limit.password.identifier-max-attempts | 5 | 单个登录标识在窗口内最大密码登录尝试次数 |
spring.open.iam.auth.login.rate-limit.password.identifier-window | 1m | 登录标识限流窗口 |
spring.open.iam.auth.login.rate-limit.password.ip-max-attempts | 20 | 单个 IP 在窗口内最大密码登录尝试次数 |
spring.open.iam.auth.login.rate-limit.password.ip-window | 1m | IP 登录限流窗口 |
spring.open.iam.auth.login.rate-limit.register.enabled | true | 是否启用账号注册限流 |
spring.open.iam.auth.login.rate-limit.register.identifier-max-attempts | 3 | 单个注册标识在窗口内最大尝试次数 |
spring.open.iam.auth.login.rate-limit.register.identifier-window | 1m | 注册标识限流窗口 |
spring.open.iam.auth.login.rate-limit.register.ip-max-attempts | 10 | 单个 IP 在窗口内最大注册尝试次数 |
spring.open.iam.auth.login.rate-limit.register.ip-window | 1m | IP 注册限流窗口 |
spring.open.iam.auth.login.verification.sms.enabled | true | 是否启用短信验证码入口 |
spring.open.iam.auth.login.verification.sms.ttl | 5m | 短信验证码有效期 |
spring.open.iam.auth.login.verification.sms.mobile-max-attempts | 1 | 单个手机号在窗口内最大发送次数 |
spring.open.iam.auth.login.verification.sms.verify-mobile-max-attempts | 5 | 单个手机号在窗口内最大校验次数 |
spring.open.iam.auth.login.verification.sms.provider | 空 | 短信验证码发送 Provider;为空时使用 Notification / SMS 默认路由 |
spring.open.iam.auth.login.verification.email.enabled | true | 是否启用邮箱验证码入口 |
spring.open.iam.auth.login.verification.email.ttl | 5m | 邮箱验证码有效期 |
spring.open.iam.auth.login.verification.email.email-max-attempts | 1 | 单个邮箱在窗口内最大发送次数 |
spring.open.iam.auth.login.verification.email.verify-email-max-attempts | 5 | 单个邮箱在窗口内最大校验次数 |
spring.open.iam.auth.login.verification.email.subject | Verification code | 未配置模板时使用的邮件标题 |
spring.open.iam.auth.login.captcha.enabled | false | 是否启用验证码风控 |
spring.open.iam.auth.login.captcha.risk-based | true | 是否仅在检测到登录风险时展示验证码 |
spring.open.iam.auth.login.captcha.sms-enabled | false | 是否允许登录页使用短信验证码校验 |
spring.open.iam.auth.login.captcha.email-enabled | false | 是否允许登录页使用邮箱验证码校验 |
spring.open.iam.auth.login.captcha.image-enabled | false | 是否允许登录页使用图形验证码校验 |
spring.open.iam.auth.login.captcha.slider-enabled | false | 是否允许登录页使用滑块验证码校验 |
spring.open.iam.auth.login.captcha.rotate-enabled | false | 是否允许登录页使用旋转验证码校验 |
spring.open.iam.auth.login.captcha.drag-enabled | false | 是否允许登录页使用拖动验证码校验 |
spring.open.iam.auth.login.qr-login.enabled | true | 是否启用扫码登录能力 |
spring.open.iam.auth.login.qr-login.ttl | 5m | 扫码登录请求有效期 |
spring.open.iam.auth.login.qr-login.default-target-client | admin | 扫码登录默认目标客户端 |
spring.open.iam.providers.github.enabled | false | 是否启用 GitHub OAuth provider |
spring.open.iam.providers.github.client-id | 空 | GitHub OAuth App Client ID |
spring.open.iam.providers.github.client-secret | 空 | GitHub OAuth App Client Secret |
spring.open.iam.providers.github.state-ttl | 5m | GitHub OAuth state 有效期 |
spring.open.iam.providers.google.enabled | false | 是否启用 Google OAuth provider |
spring.open.iam.providers.google.client-id | 空 | Google OAuth Client ID |
spring.open.iam.providers.google.client-secret | 空 | Google OAuth Client Secret |
spring.open.iam.providers.google.state-ttl | 5m | Google OAuth state 有效期 |
spring.open.iam.providers.oidc.enabled | false | 是否启用通用 OIDC provider |
spring.open.iam.providers.oidc.client-id | 空 | OIDC Client ID |
spring.open.iam.providers.oidc.client-secret | 空 | OIDC Client Secret;公共客户端可为空 |
spring.open.iam.providers.oidc.scope | openid profile email | OIDC 授权 scope |
spring.open.iam.providers.oidc.authorize-uri | 空 | 授权端点;为空时从 issuer-uri discovery 获取 |
spring.open.iam.providers.oidc.token-uri | 空 | Token 端点;为空时从 issuer-uri discovery 获取 |
spring.open.iam.providers.oidc.user-info-uri | 空 | UserInfo 端点;为空时从 issuer-uri discovery 获取 |
spring.open.iam.providers.oidc.properties.issuer-uri | 空 | OIDC issuer 地址,用于读取 /.well-known/openid-configuration |
spring.open.iam.providers.oidc.properties.identifier-field | sub | UserInfo 中稳定用户标识字段 |
spring.open.iam.providers.oidc.properties.nickname-field | name | UserInfo 中昵称字段 |
spring.open.iam.providers.oidc.properties.avatar-field | picture | UserInfo 中头像字段 |
spring.open.iam.providers.oidc.properties.email-field | email | UserInfo 中邮箱字段 |
spring.open.iam.providers.oidc.properties.mobile-field | phone_number | UserInfo 中手机号字段 |
spring.open.iam.providers.oidc.properties.pkce | false | 是否启用 OIDC PKCE S256;启用后授权地址会带 code_challenge,回调换 token 会带 code_verifier |
spring.open.iam.providers.alipay.enabled | false | 是否启用支付宝登录 |
spring.open.iam.providers.alipay.client-id | 空 | 支付宝应用 AppID |
spring.open.iam.providers.alipay.client-secret | 空 | 支付宝应用私钥 |
spring.open.iam.providers.alipay.scope | auth_user | 支付宝授权 scope |
spring.open.iam.providers.alipay.properties.alipay-public-key | 空 | 支付宝公钥,用于验签 |
spring.open.iam.providers.weibo.enabled | false | 是否启用微博登录 |
spring.open.iam.providers.weibo.client-id | 空 | 微博应用 App Key |
spring.open.iam.providers.weibo.client-secret | 空 | 微博应用 App Secret |
spring.open.iam.providers.weibo.scope | 空 | 微博授权 scope,按开放平台申请能力填写 |
spring.open.iam.providers.baidu.enabled | false | 是否启用百度登录 |
spring.open.iam.providers.baidu.client-id | 空 | 百度应用 API Key |
spring.open.iam.providers.baidu.client-secret | 空 | 百度应用 Secret Key |
spring.open.iam.providers.baidu.scope | basic | 百度授权 scope |
spring.open.iam.providers.douyin.enabled | false | 是否启用抖音登录 |
spring.open.iam.providers.douyin.client-id | 空 | 抖音应用 Client Key |
spring.open.iam.providers.douyin.client-secret | 空 | 抖音应用 Client Secret |
spring.open.iam.providers.douyin.scope | user_info | 抖音授权 scope |
spring.open.iam.providers.gitee.enabled | false | 是否启用 Gitee OAuth provider |
spring.open.iam.providers.gitee.client-id | 空 | Gitee OAuth App Client ID |
spring.open.iam.providers.gitee.client-secret | 空 | Gitee OAuth App Client Secret |
spring.open.iam.providers.gitee.scope | user_info emails | Gitee 授权 scope |
spring.open.iam.providers.qq.enabled | false | 是否启用 QQ 互联 OAuth provider |
spring.open.iam.providers.qq.client-id | 空 | QQ 互联 App ID |
spring.open.iam.providers.qq.client-secret | 空 | QQ 互联 App Key |
spring.open.iam.providers.qq.scope | get_user_info | QQ 授权 scope;如已申请 unionId,可设置 properties.union-id=true |
spring.open.iam.providers.wechat-open.enabled | false | 是否启用微信开放平台网站应用登录 |
spring.open.iam.providers.wechat-open.client-id | 空 | 微信开放平台 AppID |
spring.open.iam.providers.wechat-open.client-secret | 空 | 微信开放平台 AppSecret |
spring.open.iam.providers.wechat-open.scope | snsapi_login | 微信开放平台授权 scope |
spring.open.iam.providers.wechat-open.properties.lang | zh_CN | 微信用户信息语言 |
spring.open.iam.providers.wechat-mp.enabled | false | 是否启用微信公众号 OAuth 登录 |
spring.open.iam.providers.wechat-mp.client-id | 空 | 微信公众号 AppID |
spring.open.iam.providers.wechat-mp.client-secret | 空 | 微信公众号 AppSecret |
spring.open.iam.providers.wechat-mp.scope | snsapi_userinfo | 微信公众号授权 scope |
spring.open.iam.providers.dingtalk.enabled | false | 是否启用钉钉扫码登录 |
spring.open.iam.providers.dingtalk.client-id | 空 | 钉钉 App ID / App Key |
spring.open.iam.providers.dingtalk.client-secret | 空 | 钉钉 App Secret |
spring.open.iam.providers.dingtalk.properties.ding-talk-corp-id | 空 | 钉钉组织 ID,可选 |
spring.open.iam.providers.feishu.enabled | false | 是否启用飞书 OAuth 登录 |
spring.open.iam.providers.feishu.client-id | 空 | 飞书 App ID |
spring.open.iam.providers.feishu.client-secret | 空 | 飞书 App Secret |
spring.open.iam.providers.wecom.enabled | false | 是否启用企业微信 Web 登录 |
spring.open.iam.providers.wecom.client-id | 空 | 企业微信 CorpID |
spring.open.iam.providers.wecom.client-secret | 空 | 企业微信应用 Secret |
spring.open.iam.providers.wecom.scope | snsapi_privateinfo | 企业微信授权 scope |
spring.open.iam.providers.wecom.properties.agent-id | 空 | 企业微信应用 AgentID |
spring.open.iam.providers.telegram.enabled | false | 是否启用 Telegram Login Widget 登录 |
spring.open.iam.providers.telegram.bot-token | 空 | Telegram Bot Token,用于校验 Login Widget hash |
spring.open.iam.providers.telegram.bot-username | 空 | Telegram Bot Username,用于前端渲染 Login Widget;可带 @,接口响应会去掉前缀 |
spring.open.iam.providers.telegram.button-size | large | Telegram Login Widget 按钮尺寸,可选 large、medium、small |
spring.open.iam.providers.telegram.corner-radius | 空 | Telegram Login Widget 按钮圆角,留空使用 Telegram 默认样式 |
spring.open.iam.providers.telegram.request-access | 空 | Telegram Login Widget request_access 参数,例如 write |
spring.open.iam.providers.telegram.auth-date-ttl-seconds | 86400 | Telegram auth_date 有效期,单位秒 |
spring.open.iam.providers.evm-wallet.enabled | true | 是否启用 EVM 钱包登录 |
spring.open.iam.providers.evm-wallet.default-chain-type | evm | 默认钱包链类型 |
spring.open.iam.providers.evm-wallet.challenge-ttl | 5m | 钱包 challenge 有效期 |
spring.open.iam.providers.evm-wallet.auto-create-user | true | 钱包身份不存在时是否自动创建用户 |
spring.open.iam.providers.<code>.flow | 空 | 自定义身份提供方 flow,例如 ldap-password、saml-assertion 或企业 OIDC 的 authorization-code |
spring.open.iam.providers.<code>.protocol | 空 | 自定义身份提供方协议,例如 ldap、ad、saml2、oidc |
spring.open.iam.providers.<code>.auto-create-user | true | 外部身份不存在时是否自动创建统一账号 |
spring.open.iam.providers.<code>.ldap.url | 空 | LDAP / AD 服务地址 |
spring.open.iam.providers.<code>.ldap.manager-dn | 空 | LDAP / AD 管理员 DN;配置后先搜索用户 DN 再用用户密码 bind |
spring.open.iam.providers.<code>.ldap.manager-password | 空 | LDAP / AD 管理员密码 |
spring.open.iam.providers.<code>.ldap.base-dn | 空 | LDAP / AD 用户搜索基础 DN |
spring.open.iam.providers.<code>.ldap.user-dn-pattern | 空 | 不使用管理员搜索时的用户 DN 模板 |
spring.open.iam.providers.<code>.ldap.user-search-filter | (uid={username}) | 用户搜索过滤器 |
spring.open.iam.providers.<code>.saml.issuer | 空 | SAML Issuer;配置后必须和断言 Issuer 完全一致 |
spring.open.iam.providers.<code>.saml.audience | 空 | Service Provider audience / entity ID |
spring.open.iam.providers.<code>.saml.certificate | 空 | IdP X.509 证书,支持 PEM 或纯 Base64 DER |
spring.open.iam.providers.<code>.saml.require-signed-assertion | true | 是否要求 Assertion 自身带有效 XML Signature |
spring.open.iam.providers.<code>.saml.clock-skew | 2m | SAML NotBefore / NotOnOrAfter 时间窗口容忍偏移 |
spring.open.iam.providers.<code>.saml.assertion-field | samlResponse | 外部登录 payload 中承载 SAMLResponse 的字段名 |
spring.open.iam.providers.<code>.saml.identifier-attribute | 空 | 稳定身份标识属性;为空时使用 Subject NameID |
Sa-Token 原生配置放在 sa-token.* 下,例如 token 名称、token 前缀、过期时间和 JWT 签名密钥。JWT 密钥由 Auth starter 统一决策并同步到 Sa-Token 配置,读取顺序是:
- 显式专用密钥:
SPRING_OPEN_AUTH_JWT_SECRET_KEY或自定义的sa-token.jwt-secret-key - 显式
APP_SECRET(应用共享密钥) - 数据库配置
sa-token.jwt-secret-key - 数据库配置
spring.application.secret(Config 模块首次启动自动播种的高熵随机共享密钥) - 本地派生共享密钥
spring.application.secret(应用名 + 节点标识派生) - 生成进程内随机密钥,不写入文件
默认项目无需手动配置 JWT 专用密钥。启用数据库配置中心时,框架首次启动会自动在配置中心播种一条高熵随机的 spring.application.secret:多实例只要共享同一数据库,就会零配置收敛到同一密钥,且跨重启稳定。无数据库配置时回退到本地派生共享密钥(节点标识默认来自首启生成的 storage/runtime/device-id 随机文件,跨重启稳定)。JWT 不回退到 spring.datasource.password,避免数据库口令与 token 签名密钥在模块间散落共用;未求值的 SpEL 字面量(含 #{ 的值)会被拒绝,不会被当作密钥。
注意两点:只有最终落到进程内随机密钥(极端场景:无数据库配置且节点派生不可用)时,应用重启后历史 token 才会失效,此时会输出启动 WARN;多实例部署若不共享数据库配置中心,必须显式配置共享的 SPRING_OPEN_AUTH_JWT_SECRET_KEY 或强随机 APP_SECRET,否则各实例签发的 token 互不兼容。
安全约定
- 不创建默认账号和默认密码。
- 不把样例账号当作框架固定凭据;本地样例填充按钮默认不应在生产环境开启。
- 不在接口中返回密码哈希、token 明文或私钥。
- OAuth 登录必须先验证第三方凭证。
- OIDC 当前使用 Authorization Code + UserInfo 接入统一账号,并支持可选 PKCE S256;企业 OIDC 可通过自定义 provider code 复用该链路。需要 ID Token 验签、JWKS 缓存或企业级 SSO Server 时,应通过 provider 专用实现继续增强。
- SAML 登录默认要求 Assertion 自身带有效 XML Signature,并使用配置的 IdP 证书验签;本地联调可关闭签名要求,生产环境不建议关闭。
- 国内 OAuth provider 当前复用成熟生态实现,但隐藏在项目
OAuthProviderSPI 后面;业务代码只感知alipay、weibo、baidu、douyin、gitee、qq、wechat-open、wechat-mp、dingtalk、feishu、wecom这些身份提供方名称。 - 钱包登录必须先生成服务端 challenge,再校验签名。
- Web3 SDK 依赖只允许出现在 Web3 starter 中。
- Controller 不写业务逻辑,登录、身份绑定、授权、审计都在 Service 或 starter manager 中处理。
- 写接口必须走权限校验和操作日志。
- 涉及目标用户的数据必须通过显式数据范围校验。
后续方向
当前阶段已经完成统一账号、统一 token、RBAC、profile、数据范围和审计主线。
后续继续增强:
- 更多 OAuth provider 可继续通过
OAuthProviderSPI 接入;后续可按使用频率和依赖边界补充淘宝、京东、小米、华为等平台。 - OIDC 深度增强:ID Token 验签、JWKS 缓存、企业 SSO Server / Authorization Server。
- Passkey。
- 更完整的数据权限规则和后台 UI 元数据。