Files
Edu/services/iam/docs/nextstep-v2.md
SpecialX ad39a3bb0f feat(iam): v2 用户管理 RPC + F12 httpOnly Cookie
admin-portal §2.3 P1 阻塞项补齐:CreateUser/UpdateUser/DeleteUser 3 RPC

iam.repository/service/grpc.controller 实现 3 用户管理方法(含 bcrypt + 审计)

iam.controller 新增 POST /v1/iam/users + DELETE /v1/iam/users/:id(@RequirePermission(IAM_USER_MANAGE))

iam.dto 新增 createUserSchema Zod 校验

F12 httpOnly Cookie:refresh_token 改为 httpOnly+Secure+SameSite=Strict Cookie 下发

extractRefreshToken 优先读 cookie 回退 body + buildRefreshTokenCookie/buildClearCookie
2026-07-14 22:59:28 +08:00

26 KiB
Raw Blame History

iam 下一步工作与上下游依赖Next Steps v2

模块iam身份与访问管理服务端口 3002 HTTP + 50052 gRPC 更新日期2026-07-14 关联文档:


1. v2 迭代已完成工作

1.1 admin-portal §2.3 P1 阻塞项补齐(用户管理 RPC

# 工作项 状态
1 iam.proto 新增 CreateUserRequest / UpdateUserRequest / DeleteUserRequest / DeleteUserResponse 4 个 message
2 iam.proto service IamService 声明 CreateUser / UpdateUser / DeleteUser 3 个 RPC
3 iam.repository.ts 实现 createUser / updateUser / deleteUser 数据访问
4 iam.service.ts 实现 createUser / updateUser / deleteUser 业务逻辑(含 bcrypt + 审计)
5 iam.grpc.controller.ts 新增 3 个 @GrpcMethod 实现
6 iam.controller.ts 新增 POST /v1/iam/users + DELETE /v1/iam/users/:id REST 端点(@RequirePermission(IAM_USER_MANAGE)
7 iam.dto.ts 新增 createUserSchema Zod 校验

依据架构裁决 F12refresh_token 不再通过响应体明文返回,改为 httpOnly + Secure + SameSite=Strict Cookie 下发。

# 工作项 状态
1 iam.controller.ts 新增 extractRefreshToken(req):优先读 cookie回退 body
2 iam.controller.ts 新增 buildRefreshTokenCookie(token, maxAge) 构造 Set-Cookie
3 iam.controller.ts 新增 buildClearCookie() 构造清除 Cookie
4 POST /v1/iam/register 下发 Set-Cookie
5 POST /v1/iam/login 下发 Set-Cookie
6 POST /v1/iam/refresh 优先从 cookie 读取 + 轮换 Cookie
7 POST /v1/iam/logout 清除 CookieMax-Age=0

Cookie 属性:HttpOnly; SameSite=Strict; Max-Age=604800; Path=/; Secure

1.3 Docker 构建修复bcrypt 原生模块)

# 工作项 状态
1 Dockerfile 改用 pnpm install --no-frozen-lockfile --filter @edu/iam-service... 过滤安装策略
2 不再使用 --ignore-scripts,让 bcrypt install script 正常执行原生编译
3 仅安装 iam + workspace 依赖,避免 arch-scan 的 better-sqlite3 原生编译
4 镜像 edu/iam:test 构建成功660MBbcrypt .node 文件存在

1.4 本地 Docker 端到端验证2026-07-14

测试环境:edu-iam-test 容器NODE_ENV=production, DEV_MODE=true接入 edu-full_default 网络,直连 edu-mysql / edu-redis / edu-kafka。

JWT RS256 密钥对挂载在 edu-iam-keys volume/app/keys/private.pem + /app/keys/public.pem)。

验证项 状态 说明
GET /healthz 200 {"status":"ok","service":"iam"}
GET /readyz 2005 项依赖全 okdatabase / redis / kafka / jwks / grpc
GET /v1/iam/.well-known/jwks.json 200返回 RSA 公钥kid: iam-rs256-v1, alg: RS256, kty: RSA
POST /v1/iam/register 201 Created + Set-Cookie: refresh_token=...; HttpOnly; SameSite=Strict; Max-Age=604800; Path=/; Secure
POST /v1/iam/login 201 Created + Set-Cookie 下发
POST /v1/iam/refreshcookie 优先) 201 Created + 轮换 Set-Cookie新 jti
POST /v1/iam/logout 201 Created + Set-Cookie: refresh_token=; HttpOnly; SameSite=Strict; Max-Age=0; Path=/; Secure(清除)
POST /v1/iam/userscreateUser 201 Created + 返回新用户(含 dataScope
DELETE /v1/iam/users/:iddeleteUser 200 OK + {"success":true,"data":{"success":true}}

2. 上游依赖iam 依赖谁)

iam 作为身份基础设施服务,运行时依赖以下组件(与 v1 一致,无新增):

2.1 基础设施

组件 端点 用途 状态
MySQL mysql://edu:changeme@edu-mysql:3306/next_edu_cloud 用户/角色/权限/视口/审计/Outbox 表持久化
Redis redis://edu-redis:6379 权限缓存TTL 5min+ token 黑名单
Kafka kafka:29092 Outbox 事件投递
JWT RS256 密钥 /app/keys/private.pem + /app/keys/public.pemedu-iam-keys volume RS256 签发 + JWKS 暴露公钥

2.2 共享包(协调 AI 维护)

依赖内容 状态
shared-proto packages/shared-proto/proto/iam.proto15 RPC + 全部 message
shared-ts @edu/shared-ts/outbox OutboxModule + OutboxService

3. 下游依赖(谁依赖 iam

3.1 api-gatewayai01 负责)— P0

# 依赖项 用途 状态
1 GET /v1/iam/.well-known/jwks.json RS256 公钥集JWKSTTL 5min 缓存 已就绪
2 RS256 JWT 签发 api-gateway 用 JWKS 公钥校验 access_token 已就绪
3 GET /healthz 端点 /readyz 下游健康检查 已就绪
4 JWT claims 含 role/data_scope api-gateway 注入 x-user-roles / x-user-data-scope 头 已就绪

3.2 push-gatewayai09 负责)— P0

# 依赖项 用途 状态
1 GET /v1/iam/.well-known/jwks.json WebSocket /ws 连接时校验客户端 JWT 签名 已就绪
2 shared-go/jwks Fetcher 每 5 分钟刷新 JWKS 缓存刷新 已就绪

3.3 teacher-bffai03 负责)— P0

# 依赖项 用途 状态
1 gRPC GetUserInfo(userId) :50052 currentUser/me 查询 已就绪
2 gRPC BatchGetUsers(userIds) :50052 adminUsers 查询 已就绪
3 gRPC GetEffectivePermissions(userId) :50052 用户有效权限列表 已就绪
4 gRPC GetEffectiveDataScope(userId) :50052 用户数据范围 已就绪
5 gRPC GetViewports(userId) :50052 adminViewports 查询 已就绪
6 gRPC GetPublicKey() :50052 RS256 公钥 已就绪
7 gRPC GetChildrenByParent(parentId) :50052 家长端学生列表 已就绪
8 gRPC CreateUser / UpdateUser / DeleteUser 用户管理 CRUDadmin-portal 已就绪
9 REST 全套 RBAC CRUDroles/permissions/viewports/audit admin-portal 管理 已就绪

3.4 student-bffai04 负责)— P0

# gRPC 方法 用途 状态
1 GetUserProfile myProfile Query 已就绪
2 UpdateProfile updateProfile Mutation 已就绪
3 ChangePassword changePassword Mutation 已就绪

3.5 parent-bffai05 负责)— P0

# RPC 方法 用途 状态
1 getUserInfo(userId) 获取家长个人信息 已就绪
2 getChildrenByParent(parentId) 获取家长绑定的孩子列表 已就绪
3 getViewports(userId) 获取家长可见视口 已就绪
4 getEffectivePermissions(userId) 获取家长有效权限 已就绪
5 GET /healthz 端点 /readyz 下游健康检查 已就绪

3.6 admin-portalai16 负责)— P0

# 依赖项 用途 状态
1 gRPC CreateUser / UpdateUser / DeleteUser 用户管理 CRUDv2 新增) 已就绪
2 REST POST /v1/iam/users + DELETE /v1/iam/users/:id 用户管理 REST 端点v2 新增) 已就绪
3 GET /v1/iam/users 用户管理列表(分页 + 搜索) 已就绪
4 PATCH /v1/iam/users/:id 用户更新 已就绪
5 PATCH /v1/iam/users/:id/status 用户状态切换 已就绪
6 POST/PATCH/DELETE /v1/iam/roles 角色 CRUD 已就绪
7 POST/PATCH/DELETE /v1/iam/permissions 权限 CRUD 已就绪
8 POST/PATCH/DELETE /v1/iam/viewports 视口 CRUD 已就绪
9 POST /v1/iam/totp/enable / verify / disable TOTP 2FA 管理 已就绪
10 GET /v1/iam/audit 审计日志查询 已就绪

3.7 teacher-portalai13 负责)— P1

# 依赖项 用途 状态
1 JWT RS256 签发 登录后获取 access_token + refresh_token 已就绪
2 F12 httpOnly Cookie 模式 refresh_token 通过 Set-Cookie 下发v2 新增) 已就绪
3 POST /v1/iam/login 教师登录 已就绪
4 POST /v1/iam/refresh 刷新 token从 cookie 读取) 已就绪
5 POST /v1/iam/logout 登出(清除 cookie 已就绪
6 GET /v1/iam/me 获取当前用户信息 已就绪
7 GET /v1/iam/viewports 获取视口配置 已就绪
8 GET /v1/iam/permissions/effective 获取有效权限 已就绪

3.8 student-portalai14 负责)— P1

# 依赖项 用途 状态
1 JWT RS256 签发 学生登录 已就绪
2 F12 httpOnly Cookie 模式 refresh_token 通过 Set-Cookie 下发v2 新增) 已就绪
3 POST /v1/iam/login 学生登录 已就绪
4 PATCH /v1/iam/me 更新个人资料 已就绪
5 POST /v1/iam/change-password 修改密码 已就绪

3.9 parent-portalai15 负责)— P1

# 依赖项 用途 状态
1 JWT RS256 签发 家长登录 已就绪
2 F12 httpOnly Cookie 模式 refresh_token 通过 Set-Cookie 下发v2 新增) 已就绪
3 POST /v1/iam/login 家长登录 已就绪
4 GET /v1/iam/children 获取绑定的学生列表 已就绪
5 GET /v1/iam/me 获取家长个人信息 已就绪

3.10 同级服务

# 模块 依赖项 用途 状态
1 ai gRPC GetEffectiveDataScope(userId) 解析用户可见数据范围DataScope 6 级) 已就绪
2 core-edu gRPC BatchGetUsers / GetUserInfo DashboardService 学生计数P4+ 未来工作) 已就绪
3 msg Kafka 事件 edu.iam.user.events 用户注册/登录/角色变更事件msg 消费方需对齐) 已就绪
4 data-ana gRPC GetEffectiveDataScope(userId) 数据分析查询过滤范围 已就绪
5 content 无直接依赖

4. 需要上下游实现的协调事项

4.1 上游Gateway / BFF / Portal需实现

# 协调项 对端模块 说明
1 F12 Cookie 透传:Cookie / Set-Cookie 头透传 api-gateway / teacher-bff / student-bff / parent-bff F12 httpOnly cookie 模式要求 Gateway 和 BFF 透传 Cookie 请求头和 Set-Cookie 响应头。Gateway 当前已透传 x-user-* 头,需确认 Cookie 头也在透传白名单中。BFF GraphQL 网关需在 fetch 调用 iam 时带上客户端的 Cookie 头。
2 Cookie Domain 配置 api-gateway 生产环境跨子域共享 cookie 时,需在 iam 环境变量 REFRESH_TOKEN_COOKIE_DOMAIN 配置根域(如 .edu.example.com)。当前默认不设置 Domain。
3 HTTPS 终止点确认 api-gateway / push-gateway Cookie 带 Secure 属性,浏览器仅在 HTTPS 下发送。开发环境HTTP下浏览器不会自动回传 cookie需用 curl/Postman 手动传递。生产环境 api-gateway 必须做 HTTPS 终止。
4 admin-portal 切换到 CreateUser/DeleteUser RPC admin-portal / teacher-bff admin-portal 当前可能使用 PATCH /v1/iam/users/:id 更新用户。新增用户需切换到 gRPC CreateUser(通过 teacher-bff 聚合)或 REST POST /v1/iam/users(通过 gateway 透传)。删除用户使用 DELETE /v1/iam/users/:id
5 Kafka 消费者对齐 edu.iam.user.events topic msg / data-ana iam 发布事件到 edu.iam.user.events topic事件类型UserRegistered / UserLoggedIn / UserLoggedOut / UserRoleChanged / UserCreated / UserDeleted。msg 服务的 topic 命名期望为 edu.identity.user.*,需协调统一命名。
6 Kafka 消费者对齐 edu.iam.role.events topic msg / data-ana 事件类型:RoleCreated / RoleUpdated / RolePermissionsChanged。下游消费方需订阅此 topic 同步权限缓存。
7 Kafka 消费者对齐 edu.iam.audit.created topic data-ana 审计日志事件data-ana 可消费后入 ClickHouse 供长期分析。
8 api-gateway JWKS 缓存刷新策略 api-gateway iam JWKS 端点返回 kid: iam-rs256-v1,公钥轮换时 kid 会变。api-gateway 需在 JWT 验签失败时触发 JWKS 重新拉取cache miss 回源)。

4.2 下游(同级服务)需实现

# 协调项 对端模块 说明
1 msg 服务消费 edu.iam.user.events 创建用户消息视图 msg iam 发布 UserRegistered / UserCreated 事件后msg 服务需消费并为新用户初始化消息收件箱 / 通知偏好。事件 payload 见 shared/outbox/event-types.ts
2 data-ana 消费 edu.iam.audit.created 入 ClickHouse data-ana iam 审计日志通过 Outbox 投递到 Kafkadata-ana 需消费并写入 ClickHouse audit_logs 表供长期留存与查询。
3 core-edu DashboardService 调用 iam gRPC BatchGetUsers core-edu P4+ 工作core-edu 仪表盘需展示学生/教师计数,通过 iam gRPC BatchGetUsers 批量查询用户信息。gRPC target: iam:50052
4 ai 服务调用 iam gRPC GetEffectiveDataScope ai ai 服务在生成个性化内容时需解析用户数据范围,调用 GetEffectiveDataScope(userId) 返回 {level, scope_ids, school_id}。gRPC target: iam:50052

4.3 事件契约Kafka topic 与 payload

iam 通过 Outbox 模式发布以下事件,下游消费方需按此契约实现 consumer

Topic 事件类型 触发时机 关键字段
edu.iam.user.events UserRegistered POST /v1/iam/register userId, email, name, timestamp
edu.iam.user.events UserLoggedIn POST /v1/iam/login userId, email, ip, userAgent, timestamp
edu.iam.user.events UserLoggedOut POST /v1/iam/logout userId, timestamp
edu.iam.user.events UserCreated POST /v1/iam/users userId, email, name, dataScope, operatorId, timestamp
edu.iam.user.events UserUpdated PATCH /v1/iam/users/:id userId, changedFields, operatorId, timestamp
edu.iam.user.events UserDeleted DELETE /v1/iam/users/:id userId, operatorId, timestamp
edu.iam.user.events UserRoleChanged 角色授权/撤销 userId, roleId, permissionId, action, operatorId, timestamp
edu.iam.role.events RoleCreated POST /v1/iam/roles roleId, name, level, operatorId, timestamp
edu.iam.role.events RoleUpdated PATCH /v1/iam/roles/:id roleId, changedFields, operatorId, timestamp
edu.iam.role.events RolePermissionsChanged PATCH roles/:id/permissions roleId, permissionIds, operatorId, timestamp
edu.iam.audit.created AuditLogCreated 所有审计操作 auditId, userId, action, resource, ip, userAgent, timestamp

事件 envelope 格式遵循 @edu/shared-ts/outbox{event_id, event_type, aggregate_id, payload, occurred_at, version}

幂等性:消费方需基于 event_id 去重Redis SETNX 或 DB 唯一索引)。


5. iam 自身待办(非阻塞)

# 工作项 优先级 阻塞条件
1 单元测试 + 集成测试覆盖率 ≥ 80% P2
2 生产环境 JWT 密钥轮换流程kid 变更 + JWKS 缓存失效广播) P3 生产部署前
3 Kafka topic 创建自动化edu.iam.user.events / role.events / audit.created P3 K8s 部署阶段
4 数据库迁移脚本drizzle-kit P3 生产部署前
5 UpdateUser REST 端点补齐(当前仅有 PATCH /v1/iam/users/:id无 POST /v1/iam/users/:id 通用更新) P3
6 Cookie Domain 环境变量配置化(REFRESH_TOKEN_COOKIE_DOMAIN P3 生产部署前

6. 联调待办

# 联调项 对端模块 状态
1 api-gateway JWKS 真实验签 api-gateway (ai01) 已就绪
2 push-gateway JWKS 真实验签 push-gateway (ai09) 已就绪
3 teacher-bff gRPC 全量调用(含 CreateUser/DeleteUser teacher-bff (ai03) 待联调
4 student-bff GetUserProfile/UpdateProfile/ChangePassword student-bff (ai04) 已就绪
5 parent-bff getUserInfo/getChildrenByParent/getViewports parent-bff (ai05) 已就绪
6 admin-portal RBAC CRUD + 用户管理 RPC 全量 admin-portal (ai16) 待联调
7 teacher-portal F12 cookie 模式联调 teacher-portal (ai13) 待联调
8 student-portal F12 cookie 模式联调 student-portal (ai14) 待联调
9 parent-portal F12 cookie 模式联调 parent-portal (ai15) 待联调
10 msg 消费 edu.iam.user.events 事件 msg 待联调
11 data-ana 消费 edu.iam.audit.created 事件 data-ana 待联调

iam 服务 v2 迭代完成。所有 v2 阻塞项admin-portal 用户管理 RPC + F12 cookie 模式 + Docker bcrypt 修复)已就绪,下游模块可开始 v2 联调。