feat(iam): 完整实现 iam 身份认证与权限服务
包含 jwt/jwks/audit/grpc、rbac、cache、redis/kafka 配置等完整实现
This commit is contained in:
@@ -1,44 +1,135 @@
|
||||
# IAM Service
|
||||
|
||||
身份与访问管理服务(Identity and Access Management),P2 身份阶段交付。
|
||||
身份与访问管理服务(Identity and Access Management)。
|
||||
|
||||
> 版本:v0.2.0(对齐 coord-final-decisions I1-I8 + president §2.15/§2.16/§3.2/§5.5)
|
||||
> 详细设计见 [02-architecture-design.md](./docs/02-architecture-design.md)
|
||||
|
||||
## 职责
|
||||
|
||||
- 用户注册 / 登录 / 刷新令牌
|
||||
- 角色(Role)与权限(Permission)管理
|
||||
- Access / Refresh Token 签发与校验
|
||||
- 用户信息查询(供 BFF / Gateway 聚合)
|
||||
- 用户注册 / 登录 / 刷新令牌 / 登出
|
||||
- JWT RS256 签发(access 15min + refresh 7day,jti 黑名单)
|
||||
- 角色(Role)与权限(Permission)管理(三层角色模型:system/organization/temporary)
|
||||
- DataScope 6 级数据范围(self/subject/class/grade/school/all)
|
||||
- 视口(Viewport)配置查询
|
||||
- 学生-家长关系管理(`GetChildrenByParent`)
|
||||
- 审计日志(`iam_user_audit_log` + `AuditCreated` 事件)
|
||||
- JWKS 公钥暴露(供 api-gateway 验签)
|
||||
- 双入口:REST `/v1/iam/*` + gRPC 50052(package `next_edu_cloud.iam.v1`,12 RPC)
|
||||
|
||||
## 技术栈
|
||||
|
||||
- NestJS 10 + TypeScript(ESM)
|
||||
- Drizzle ORM + MySQL
|
||||
- bcrypt 密码哈希
|
||||
- jsonwebtoken(P2 骨架使用 HS256,后续切 RS256)
|
||||
- NestJS 10 + TypeScript(ESM,NodeNext)
|
||||
- Drizzle ORM + MySQL(10 张表)
|
||||
- bcrypt 密码哈希(cost ≥ 12)
|
||||
- jsonwebtoken RS256(本地文件密钥)
|
||||
- ioredis(权限缓存 TTL 5min + token jti 黑名单)
|
||||
- kafkajs + shared-ts OutboxModule(事务性事件发布)
|
||||
- @nestjs/microservices + @grpc/grpc-js(gRPC server)
|
||||
- pino 日志 / prom-client 指标 / OpenTelemetry 链路
|
||||
|
||||
## 端口
|
||||
|
||||
默认 `3002`,通过 `PORT` 环境变量覆盖。
|
||||
| 服务 | 端口 | 备注 |
|
||||
| -------- | ----- | ------------------------ |
|
||||
| iam HTTP | 3002 | REST 入口(`PORT`) |
|
||||
| iam gRPC | 50052 | gRPC 入口(`GRPC_PORT`) |
|
||||
|
||||
## API
|
||||
## 环境变量
|
||||
|
||||
| Method | Path | 说明 |
|
||||
| ------ | --------------- | --------------------------------- |
|
||||
| POST | `/iam/register` | 注册 |
|
||||
| POST | `/iam/login` | 登录 |
|
||||
| POST | `/iam/refresh` | 刷新令牌 |
|
||||
| GET | `/iam/me` | 当前用户信息(需 `x-user-id` 头) |
|
||||
| 变量 | 说明 | 默认值 |
|
||||
| ----------------------------- | ------------------------------ | ---------------- |
|
||||
| `PORT` | HTTP 端口 | `3002` |
|
||||
| `GRPC_PORT` | gRPC 端口 | `50052` |
|
||||
| `DATABASE_URL` | MySQL 连接串 | — |
|
||||
| `REDIS_URL` | Redis 连接串 | — |
|
||||
| `IAM_PRIVATE_KEY_PATH` | RS256 私钥文件路径 | — |
|
||||
| `IAM_PUBLIC_KEY_PATH` | RS256 公钥文件路径 | — |
|
||||
| `JWT_ISSUER` | JWT 签发者 | `next-edu-cloud` |
|
||||
| `JWT_AUDIENCE` | JWT 受众 | `next-edu-cloud` |
|
||||
| `JWT_KEY_ID` | 密钥 ID(kid,用于 JWKS 轮换) | `iam-rs256-v1` |
|
||||
| `ACCESS_TOKEN_TTL` | access_token TTL | `15m` |
|
||||
| `REFRESH_TOKEN_TTL_DAYS` | refresh_token TTL(天) | `7` |
|
||||
| `KAFKA_BROKERS` | Kafka broker 列表(逗号分隔) | — |
|
||||
| `KAFKA_CLIENT_ID` | Kafka client ID | `iam-service` |
|
||||
| `OTEL_EXPORTER_OTLP_ENDPOINT` | OTel OTLP 端点(可选) | — |
|
||||
| `LOG_LEVEL` | 日志级别 | `info` |
|
||||
| `NODE_ENV` | 环境标识 | `development` |
|
||||
|
||||
## REST API
|
||||
|
||||
| Method | Path | 权限 | 说明 |
|
||||
| ------ | ------------------------------- | ----------------- | ---------------------------------- |
|
||||
| POST | `/v1/iam/register` | 公开 | 注册 |
|
||||
| POST | `/v1/iam/login` | 公开 | 登录 |
|
||||
| POST | `/v1/iam/refresh` | 公开 | 刷新令牌(轮换 + 旧 jti 加黑名单) |
|
||||
| POST | `/v1/iam/logout` | `IAM_USER_READ` | 登出 |
|
||||
| GET | `/v1/iam/me` | `IAM_USER_READ` | 当前用户信息 |
|
||||
| GET | `/v1/iam/viewports` | `IAM_USER_READ` | 当前用户视口 |
|
||||
| GET | `/v1/iam/permissions/effective` | `IAM_USER_READ` | 当前用户有效权限 |
|
||||
| GET | `/v1/iam/children` | `IAM_USER_READ` | 家长的孩子列表 |
|
||||
| GET | `/v1/iam/roles` | `IAM_ROLE_MANAGE` | 角色列表 |
|
||||
| GET | `/v1/iam/permissions` | `IAM_ROLE_MANAGE` | 权限点列表 |
|
||||
| GET | `/v1/iam/audit` | `IAM_AUDIT_READ` | 审计日志 |
|
||||
| GET | `/v1/iam/.well-known/jwks.json` | 公开 | RS256 公钥 JWK Set |
|
||||
|
||||
## gRPC API
|
||||
|
||||
12 RPC,proto 定义见 `packages/shared-proto/proto/iam.proto`,package `next_edu_cloud.iam.v1`:
|
||||
|
||||
`Register` / `Login` / `RefreshToken` / `Logout` / `GetUserInfo` / `BatchGetUsers` / `GetEffectivePermissions` / `GetEffectiveAccess` / `GetEffectiveDataScope` / `GetViewports` / `GetPublicKey` / `GetChildrenByParent`
|
||||
|
||||
## 健康检查
|
||||
|
||||
| 端点 | 用途 | 鉴权 |
|
||||
| -------------- | ------------------------------------------------- | ---- |
|
||||
| `GET /healthz` | 存活探针(liveness),仅返回进程状态,不检查依赖 | 无 |
|
||||
| `GET /readyz` | 就绪探针(readiness),检查 DB 连接,失败返回 503 | 无 |
|
||||
| 端点 | 用途 | 鉴权 |
|
||||
| -------------- | ------------------------------------------------------------------------- | ---- |
|
||||
| `GET /healthz` | 存活探针(liveness),仅返回进程状态 | 无 |
|
||||
| `GET /readyz` | 就绪探针(readiness),检查 DB/Redis/Kafka/JWKS/gRPC 5 依赖,失败返回 503 | 无 |
|
||||
|
||||
实现见 `src/shared/health/health.controller.ts`,5 个 NestJS 服务(iam/core-edu/content/msg/classes)一致。
|
||||
## 数据表(10 张)
|
||||
|
||||
## 数据表
|
||||
| 表名 | 用途 |
|
||||
| ----------------------- | --------------------------- |
|
||||
| `iam_users` | 用户主表 |
|
||||
| `iam_roles` | 角色表(role_type + level) |
|
||||
| `iam_user_roles` | 用户-角色绑定 |
|
||||
| `iam_permissions` | 权限点表 |
|
||||
| `iam_role_permissions` | 角色-权限映射 |
|
||||
| `iam_refresh_tokens` | refresh token(含 jti) |
|
||||
| `iam_role_viewports` | 角色-视口配置(含 level) |
|
||||
| `iam_student_guardians` | 学生-家长关系 |
|
||||
| `iam_user_audit_log` | 审计日志 |
|
||||
| `iam_password_history` | 密码重用限制 |
|
||||
|
||||
`iam_users` / `iam_roles` / `iam_user_roles` / `iam_permissions` / `iam_role_permissions` / `iam_refresh_tokens`
|
||||
DDL + 种子数据见 `scripts/iam-init.sql`。
|
||||
|
||||
## Kafka 事件
|
||||
|
||||
| Topic | 事件 |
|
||||
| ----------------------- | -------------- |
|
||||
| `edu.iam.user.events` | `UserCreated` |
|
||||
| `edu.iam.role.events` | `RoleChanged` |
|
||||
| `edu.iam.audit.created` | `AuditCreated` |
|
||||
|
||||
通过 shared-ts `OutboxModule` 事务性发布(at-least-once + idempotent producer)。
|
||||
|
||||
## 开发
|
||||
|
||||
```bash
|
||||
# 安装依赖
|
||||
pnpm install
|
||||
|
||||
# 初始化数据库(需先启动 MySQL + Redis + Kafka)
|
||||
mysql -u root -p < scripts/iam-init.sql
|
||||
|
||||
# 生成 RS256 密钥对
|
||||
openssl genrsa -out private.pem 2048
|
||||
openssl rsa -in private.pem -pubout -out public.pem
|
||||
|
||||
# 启动开发服务
|
||||
pnpm --filter @edu/iam-service dev
|
||||
|
||||
# 质量校验
|
||||
pnpm --filter @edu/iam-service lint
|
||||
pnpm --filter @edu/iam-service typecheck
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user