接入 WebAuthn 第二因素与 PostgreSQL 凭据仓储

This commit is contained in:
2026-09-27 18:29:52 +00:00
parent 6d040658c8
commit 45994e3919
25 changed files with 874 additions and 23 deletions
+3 -2
View File
@@ -10,8 +10,9 @@ try-with-resources 管理已认证用户仓储会话;基础设施的 `AdUserRe
`AdUserEntry` 的 LDAP 注解不会进入领域对象。
成功后重定向到 `/signin/mfa`,显示目录账号、objectGUID、邮箱、直接所属组及组 DN。
**这是密码因素验收页面,MFA 尚未接入,不是完整登录成功。** Spring Security 保存
仅含 `FACTOR_PASSWORD` 的认证结果;其余应用请求使用 `denyAll`,不调用 Hydra,
**密码成功本身不是完整登录成功。** Spring Security 先保存仅含 `FACTOR_PASSWORD`
的认证结果;启用 [WebAuthn](webauthn.md) 后在此继续第二因素,否则停留在等待页面。
未实现的应用请求使用 `denyAll`,不调用 Hydra,
不替换现役 Go/Authelia/Gitea 登录链路。
## 目录和组语义
+2
View File
@@ -29,6 +29,8 @@ WebAuthn 集成;TOTP、恢复方式与已有 Authelia MFA 的迁移方式需
- 登录 Controller 与安全链使用 `@ConditionalOnProperty(iam.ad.enabled)`;AOT 在构建时
决定 bean 是否存在。AD Native 产物必须在 AOT 阶段启用此属性,验证实际入口与兜底链,
不能假设运行时修改属性会重新装配 bean。本轮只验证 JVM,尚未验收该 Native 路径。
- WebAuthn 新增的 JDBC/Flyway/PostgreSQL、WebAuthn4J 校验与 JSON 路径本轮仅做 JVM 验证;
Native AOT 时还需启用 `iam.webauthn.enabled`,不得套用基础骨架的 Native 结论。
- 最终运行镜像无需 JRE,不允许以回退 JVM 的方式令 Native 验收通过。
- LDAP、MFA、数据库、TLS、JSON 和 Hydra HTTP 客户端全部在 Native 中执行。
- 纳入 Actuator、Micrometer Prometheus 与 OpenTelemetry/分布式追踪;实际发起请求后
+70
View File
@@ -0,0 +1,70 @@
# WebAuthn 第二因素
WebAuthn 使用 Spring Security 官方过滤器、WebAuthn4J 校验和 JDBC 仓储。
PostgreSQL 保存 user handle、凭据公钥与签名计数等记录,不保存用户密码或认证器私钥。
AD 仍为用户与组权威;凭据按目录 authority + objectGUID 关联,用户名改名不会换主体。
## 登录与注册
1. `/signin` 使用原生表单 POST 验证 AD 密码,建立 `FACTOR_PASSWORD`。
2. `/signin/mfa`:没有凭据时注册 passkey;已有凭据时验证 passkey。
3. 注册只保存凭据,必须再次实际验证,才取得 `FACTOR_WEBAUTHN`。
4. `/signin/complete` 要求两种因素均在 10 分钟内有效;目前仅显示验证结果,尚不接受 Hydra challenge。
首次注册信任近期 AD 密码验证。已有凭据后的新增注册同时要求密码与 WebAuthn 因素;
本轮 UI 只提供首次注册与验证,不提供新增管理、删除或自助恢复入口。遗失所有 passkey
尚无自助登录途径;生产上线前需要另行确定恢复和初始注册政策,不能把数据库清空作为日常恢复方式。
注册和验证均要求认证器 user verification(例如 PIN 或生物识别)。
Spring Security 负责因素合并、会话轮换、退出和 CSRF。应用仅补目录主体与凭据所有权
限制、首次注册并发检查,以及 challenge 的 5 分钟服务端有效期和单次消费。
没有应用自建登录状态机或认证 Filter。上游 options Filter 位于授权 Filter 前,因而
这些检查在 RelyingPartyOperations 扩展点执行,不能仅靠 URL 授权规则。
## 本地数据库
```sh
# 密码从 shell 或外部权限为 0600 的 env 文件提供;不要写入版本库。
docker compose -p iam-login-dev -f compose.dev.yaml up -d
```
必须设置 `IAM_DEV_DB_PASSWORD`。PostgreSQL 仅发布在 `127.0.0.1:15432`,可用
`IAM_DEV_DB_PORT` 调整端口;数据保存在 Compose named volume 中。
普通 `down` 不删数据,**不要使用 `down -v`**,否则会删除已注册凭据。
应用额外配置:
```yaml
iam:
webauthn:
enabled: true
rp-id: laptop.tail7e769.ts.net
origin: https://laptop.tail7e769.ts.net:18082
spring:
datasource:
url: jdbc:postgresql://127.0.0.1:15432/iam_login
username: iam_login
password: ${IAM_DEV_DB_PASSWORD}
```
同时启用并配置 [AD](ad-login.md)。RP ID 不含协议与端口,origin 必须与浏览器实际
HTTPS 入口完全一致;改变 RP 域名后旧凭据不能直接在新域名使用。
凭据 schema 由 Flyway 管理,采用 Spring Security 7.1.1 官方 PostgreSQL 表结构,
增加主体名称唯一约束和凭据所有者索引。未启用 WebAuthn 时不创建 DataSource,AD-only
路径不需要数据库。数据库不可用时 MFA 失败,不降级为仅密码通过。
## 验证
JVM 回归使用 Testcontainers PostgreSQL 与模拟 AD;不会向真实 AD 提交测试密码。
浏览器用 Chromium 虚拟认证器产生真实注册/断言签名,再交给后端校验:
```sh
scripts/gradle-in-docker test
scripts/gradle-in-docker webauthnBrowserFixture
# 另一终端;测试夹具固定监听 localhost:18083,使用测试证书。
IAM_WEBAUTHN_FIXTURE=1 npm --prefix frontend run test:browser -- webauthn.spec.ts
```
测试专用启动类仅在 test classpath,不进入生产 JAR,也不提供生产调试 API。
真实用户的 passkey 注册、认证器兼容性和 Native 路径仍需独立验收;JVM/虚拟认证器通过
不能代替真实人类或 Native 验收。生产共享 PostgreSQL 的接入留在部署阶段。