fhurau/distributed-auth-platform
GitHub: fhurau/distributed-auth-platform
一个基于 Spring Boot、Next.js 和 Keycloak 构建的本地全栈应用安全演示平台,完整展示身份认证、对象级授权、审计日志和安全 CI/CD 等企业级安全控制措施。
Stars: 0 | Forks: 0
# 分布式-auth-platform
distributed-auth-platform 是一个本地作品集应用程序,用于演示
使用 Next.js、Spring Boot、PostgreSQL 和 Keycloak 构建的全栈系统中的
安全控制。
该演示包含使用 PKCE 的 OAuth2/OIDC 登录、项目 CRUD、对象级
授权、审计日志、受控文档上传/下载、
Swagger/OpenAPI、安全标头以及 GitHub Actions 安全检查。

## 快速开始
前置条件:带有 Docker Compose 的 Docker Desktop,以及 Windows PowerShell 5.1 或
PowerShell 7。
从全新的克隆开始:
```
Copy-Item .env.example .env
docker compose up -d --build
```
示例环境值仅供本地演示使用。在隔离的本地机器之外使用本
仓库之前,请更改 `.env` 中的 PostgreSQL 和 Keycloak 管理员密码。
容器启动完成后,打开:
- Frontend: [http://localhost:3000](http://localhost:3000)
- Swagger UI: [http://localhost:8080/swagger-ui.html](http://localhost:8080/swagger-ui.html)
- Keycloak: [http://localhost:8081](http://localhost:8081)
- Prometheus: [http://localhost:9090](http://localhost:9090)
- Grafana: [http://localhost:3001](http://localhost:3001) (`.env` 中的 `admin` /
`GRAFANA_ADMIN_PASSWORD`)
运行自动审查检查:
```
powershell -ExecutionPolicy Bypass -File .\scripts\smoke-test.ps1
```
[冒烟测试脚本](scripts/smoke-test.ps1)验证身份验证、
对象级授权、文档限制和审计日志访问。它
使用临时数据,即使在检查失败后也会进行清理,不会打印任何 token 或
密码,并且当控制行为与预期不符时以非零状态退出。
Keycloak 仅在 `securetask` realm 不存在时才导入它。
如果存在较旧的本地 realm,请删除本地 Docker 卷,并
仅在允许删除本地演示数据时才重新启动。
## 架构
```
flowchart LR
Browser[Browser] -->|OIDC Authorization Code + PKCE| Keycloak[Keycloak]
Browser -->|Bearer token| Frontend[Next.js frontend / API proxy]
Frontend -->|Bearer token| Backend[Spring Boot API]
Backend -->|Validate JWT/JWKS| Keycloak
Backend -->|Audit events| Kafka[(Kafka)]
Kafka -->|Consume| Backend
Backend -->|Cache, token blacklist, rate limits| Redis[(Redis)]
Backend -->|JPA/Flyway| Postgres[(PostgreSQL)]
Keycloak -->|Realm/user data| Postgres
Prometheus[Prometheus] -->|Scrape /actuator/prometheus| Backend
Grafana[Grafana] -->|Query| Prometheus
```
后端既向 Kafka 发布消息,也从中消费消息:服务将
`AuditEvent` 发布到 `audit-events` topic,而不是直接写入审计行,
同一应用程序中的消费者读取该 topic 并
执行向 PostgreSQL 的实际写入(原因请参阅
[基于 Kafka 的审计事件流处理](docs/05-audit-logging.md))。
Redis 支持三个独立的功能——项目列表缓存、JWT
撤销黑名单和每用户速率限制器——而不是单一的缓存
层。
指标:后端在
`/actuator/prometheus` 暴露 Prometheus 格式的指标
(未经身份验证——请参阅
[本地演示限制](#local-demo-limitations));Prometheus 每 15 秒抓取一次;Grafana 的 "distributed-auth-platform Backend Overview" 仪表板(启动时从
[`infra/grafana/provisioning/dashboards/backend-overview.json`](infra/grafana/provisioning/dashboards/backend-overview.json)
自动配置,而非手动构建)显示请求速率、p95 延迟和错误
率。每个后端日志行都是结构化的 JSON(Elastic Common Schema),并
将 `X-Correlation-ID` 请求标头作为 `correlationId` 字段携带,因此
即使在并发流量下,也可以通过该 ID 追踪单个请求的日志行。
## Kubernetes(演示)
**本节是可选的,尝试运行该项目时不需要。**
上面的 docker-compose 是在本地运行 distributed-auth-platform 唯一受支持的方式。
[`deploy/k8s/`](deploy/k8s/) 下的清单文件是为了演示
部署就绪情况——即相同的容器可以在真实的
编排器下运行,并具备健康探针、启动顺序和服务发现——
而不是作为第二种开发依赖。
[安全工作流](.github/workflows/security-ci.yml) 中的 GitHub Actions `deploy` 作业
在每次推送/PR 时自动证明这一点:它构建后端和前端镜像,
在 runner 上启动一个一次性的 [kind](https://kind.sigs.k8s.io/)(Kubernetes-in-Docker)
集群,将镜像加载到其中(无需 registry、无需云
账户、无需任何凭据),应用 `deploy/k8s/` 中的每个清单文件,
等待所有 pod 就绪,并通过 `kubectl port-forward` 对
后端健康端点和前端进行冒烟测试。
集群和镜像在作业完成时会被丢弃。
与 compose 设置相比已知的、有意的简化(所有这些
都是因为这是一个演示,而不是生产环境的清单集合):
- 没有 `Secret` 对象——每个值都是已经在 `.env.example` 中
有意提交的演示凭据,为简单起见保存在 `ConfigMap` 中。
真正的部署应该使用 `Secret` 或外部密钥管理器。
- 没有 `PersistentVolumeClaim`——Postgres 和 Redis 数据在 pod
重启后不会保留。对于每次 CI 运行或本地演示
会话都会重新创建的集群来说没有问题;对于任何需要保留数据的情况则不合适。
- 前端的 `NEXT_PUBLIC_*` 值在
`docker build` 时被固化到镜像中(这是 Next.js 的限制,而不是 Kubernetes 的限制)——仅更改
`deploy/k8s/frontend/configmap.yaml` 无效;必须使用匹配的 `--build-arg` 值重新构建
镜像。
- `deploy/k8s/ingress.yaml` 需要一个 ingress controller(例如
`ingress-nginx`),而 kind 默认不安装它。CI 的冒烟测试
使用 `kubectl port-forward` 代替,这证明了相同的
Service 到 Pod 的路由,且设置要少得多;Ingress 清单
是为在本地安装 controller 的任何人提供的。
要在本地使用 [kind](https://kind.sigs.k8s.io/) 和
[kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl) 自行运行:
```
kind create cluster --name securetask
docker build -t securetask-backend:local ./backend
docker build `
--build-arg NEXT_PUBLIC_KEYCLOAK_URL=http://localhost:8081 `
--build-arg NEXT_PUBLIC_KEYCLOAK_REALM=securetask `
--build-arg NEXT_PUBLIC_KEYCLOAK_CLIENT_ID=securetask-frontend `
--build-arg NEXT_PUBLIC_API_PROXY_PATH=/api/backend `
-t securetask-frontend:local ./frontend
kind load docker-image securetask-backend:local --name securetask
kind load docker-image securetask-frontend:local --name securetask
kubectl apply -f deploy/k8s/namespace.yaml
kubectl create configmap keycloak-realm-import -n securetask `
--from-file=infra/keycloak/realms/securetask-realm.json `
--dry-run=client -o yaml | kubectl apply -f -
kubectl apply -R -f deploy/k8s/
kubectl -n securetask rollout status deployment/backend
kubectl -n securetask port-forward svc/frontend 3000:3000
```
如果清单的 `image:` 字段(`securetask-backend:ci` /
`securetask-frontend:ci`)与上面的标签不匹配,请编辑
`deploy/k8s/backend/deployment.yaml` 和
`deploy/k8s/frontend/deployment.yaml` 改为 `:local`,或者将您的本地构建标记为
`:ci`。
## 演示凭据
这些账户和密码是为本地演示有意提交的。
请勿在共享或生产环境中重复使用它们。
| 账户 | 密码 | 角色 | 演示内容 |
| --- | --- | --- | --- |
| `user1@example.com` | `User123!` | `USER` | 项目所有权和文档处理 |
| `user2@example.com` | `User123!` | `USER` | 拒绝跨用户访问 |
| `admin@example.com` | `Admin123!` | `ADMIN` | 管理项目和审计访问 |
| `auditor@example.com` | `Auditor123!` | `AUDITOR` | 只读审计审查 |
| `manager@example.com` | `Manager123!` | `MANAGER` | 附加演示角色 |
## 演示流程
1. 以 `user1@example.com` 身份登录,创建一个项目,并上传允许的
`.txt`、`.pdf`、`.png`、`.jpg` 或 `.jpeg` 文档。
2. 登出并以 `user2@example.com` 身份登录。尝试访问 user1 的
项目的请求将被拒绝,并返回 `403 Forbidden`。
3. 以 `admin@example.com` 或 `auditor@example.com` 身份登录,并在
审计日志页面上查看记录的事件。
4. 运行冒烟测试,以非交互方式重复执行核心的正面和负面安全检查。


## 安全设计
- 浏览器使用公共的 `securetask-frontend` OIDC 客户端,采用
Authorization Code Flow 和 S256 PKCE。
- Access token 保留在内存中,不存储在 `localStorage` 中。
- 后端验证 bearer JWT,并独立于前端强制执行项目所有权和
基于角色的访问。
- 上传限制为 5 MB,通过扩展名进行限制,以随机化
名称存储,并检查是否存在路径遍历。
- 审计记录排除了密码、token、密钥、文件内容和完整的
请求体。
- 仅限本地的 `securetask-smoke-test` Keycloak 客户端与
浏览器客户端分离。`securetask-frontend` 仍然禁用
直接访问授权。
- 前端和后端响应包含与 localhost 兼容的安全标头
基线。CSP 并非有意呈现为生产级别。
详细设计和限制:
- [架构](docs/01-architecture.md)
- [威胁模型](docs/02-threat-model.md)
- [安全控制](docs/03-security-controls.md)
- [OWASP Top 10 2025 和 API Security Top 10 2023 映射](docs/04-owasp-mapping.md)
- [审计日志](docs/05-audit-logging.md)
- [安全权衡](docs/06-security-tradeoffs.md)
- [AppSec 演示场景](docs/07-demo-scenarios.md)
- [MVP 项目计划](docs/00-project-plan.md)
## 审查清单
- [ ] 使用 `.env.example` 从全新的克隆开始。
- [ ] 确认前端将登录重定向到本地的 Keycloak realm。
- [ ] 完成 user1/user2 对象授权演示。
- [ ] 以 ADMIN 或 AUDITOR 身份审查审计事件。
- [ ] 在 [Swagger UI](http://localhost:8080/swagger-ui.html) 中检查受保护的操作。
- [ ] 运行 [`scripts/smoke-test.ps1`](scripts/smoke-test.ps1) 并确认所有检查通过。
- [ ] 审查 [AppSec 文档](docs/01-architecture.md)。
- [ ] 检查 [GitHub Actions 安全工作流](.github/workflows/security-ci.yml)。
## 安全 CI
[安全工作流](.github/workflows/security-ci.yml) 在推送到
`main`、Pull Request 和手动调度时运行。它使用免费工具,并且
不会部署到真实的基础设施——仅在 runner 本地使用一个一次性的
`kind` 集群:
- Java 21 上的 Maven 后端测试
- Node.js 上的前端锁定安装、lint 和生产构建
- Gitleaks 密钥扫描
- Trivy 高/严重级别依赖项扫描
- Actionlint 工作流验证
- Kubernetes 部署演示:构建两个镜像,将
[`deploy/k8s/`](deploy/k8s/) 应用到 `kind` 集群,并对其进行冒烟测试(请参阅
上面的 [Kubernetes(演示)](#kubernetes-demonstration))
## 本地演示限制
本仓库演示的是控制措施;它不是生产部署
蓝图。
- 服务使用普通的 HTTP 和特定于 localhost 的源。
- 演示凭据和冒烟测试密码授权客户端特意
仅限本地使用。
- 密钥通过本地 `.env` 文件提供,而不是使用密钥
管理器。
- 上传的文件使用本地文件系统存储,没有恶意软件扫描。
- PostgreSQL 和 Keycloak 使用面向开发的 Docker 配置运行。
- CSP 允许 localhost 前端、Keycloak 和
Swagger UI 所需的源;生产环境应使用 HTTPS 和特定于部署的、
基于 nonce/hash 的策略。
- 审计日志没有外部保留、警报或防篡改存储。
- `/actuator/prometheus` 未经身份验证(与
健康端点一起被允许),以便本地 Prometheus 可以在没有抓取凭据的情况下抓取它;
真正的部署会将其置于网络策略或仅限抓取的
token 之后,而不是公开暴露它。
- Prometheus 和 Grafana 在没有配置持久存储或警报规则
的情况下运行——指标在容器重新创建时重置,并且没有
alertmanager。
- 可用性、备份和密钥轮换控制超出了本演示的
范围。
## 仓库布局
```
backend/ Spring Boot resource server
frontend/ Next.js TypeScript application
infra/keycloak/ Local Keycloak realm import
infra/prometheus/ Prometheus scrape config
infra/grafana/ Grafana datasource/dashboard provisioning
deploy/k8s/ Kubernetes manifests (deployment-readiness demo, see above)
scripts/ Reviewer smoke test
docs/ Architecture and AppSec documentation
.github/workflows Security CI
```
标签:域名枚举, 子域名突变, 搜索引擎查询, 测试用例, 版权保护, 自定义请求头