diff --git a/docs/superpowers/specs/2026-06-15-auth-system-design.md b/docs/superpowers/specs/2026-06-15-auth-system-design.md new file mode 100644 index 0000000..b2e1779 --- /dev/null +++ b/docs/superpowers/specs/2026-06-15-auth-system-design.md @@ -0,0 +1,125 @@ +# 用户账号系统设计 + +**日期:** 2026-06-15 +**状态:** 已批准 + +## 背景 + +项目现状:Vue 前端 + Hono/Bun 后端 + SQLite,所有数据对任何人开放访问,无任何认证。目标是加入登录门禁,防止陌生人访问,但所有登录用户共享同一份数据。 + +## 核心决策 + +- 多用户账号,存数据库,管理员创建账号(不开放注册) +- access token(JWT,15分钟)+ refresh token(随机UUID入库,7天)双 token 方案 +- refresh token hash 后存库,支持撤销 + +--- + +## 一、数据库 Schema + +在现有 `books` 表旁新增两张表,在 `server/db.ts` 的 `SCHEMA` 中追加: + +```sql +CREATE TABLE IF NOT EXISTS users ( + id TEXT PRIMARY KEY, + username TEXT NOT NULL UNIQUE, + password_hash TEXT NOT NULL, + role TEXT NOT NULL DEFAULT 'user', + created_at TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS refresh_tokens ( + id TEXT PRIMARY KEY, + user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE, + token_hash TEXT NOT NULL, + expires_at TEXT NOT NULL, + created_at TEXT NOT NULL +); +``` + +**初始 admin 账号:** 服务启动时读取环境变量 `ADMIN_USERNAME` / `ADMIN_PASSWORD`,若 users 表中不存在该用户名则自动写入。密码用 bcrypt hash 存储。 + +--- + +## 二、服务端 API + +### 新增文件 + +- `server/auth.ts` — JWT 签发/验证、bcrypt、token CRUD 的纯函数 +- `server/middleware/auth.ts` — Hono 中间件(验证 Bearer token,注入 user 到 context) +- `server/routes/auth.ts` — `/api/auth/*` 路由 +- `server/routes/admin.ts` — `/api/admin/*` 路由 + +### 端点列表 + +| 方法 | 路径 | 说明 | 认证要求 | +|------|------|------|----------| +| POST | `/api/auth/login` | 登录,返回 access + refresh token | 无 | +| POST | `/api/auth/refresh` | 换新 access token | 无(带 refresh token) | +| POST | `/api/auth/logout` | 撤销当前 refresh token | Bearer | +| GET | `/api/auth/me` | 返回当前用户信息 | Bearer | +| GET | `/api/admin/users` | 列出所有用户 | Admin only | +| POST | `/api/admin/users` | 创建新用户 | Admin only | +| DELETE | `/api/admin/users/:id` | 删除用户 | Admin only | + +### 现有路由保护 + +`/api/books/*` 和 `/api/generate/*` 全部加上 auth 中间件,未认证返回 401。 + +### Token 规格 + +- **access token**:JWT,HS256,payload `{ userId, role, exp: now+15min }`,密钥来自环境变量 `JWT_SECRET` +- **refresh token**:`crypto.randomUUID()`,SHA-256 hash 后存 `refresh_tokens` 表,原始值返回给客户端 + +--- + +## 三、前端 + +### 新增文件 + +- `src/composables/useAuth.ts` — token 管理、登录/登出/刷新逻辑 +- `src/components/LoginPage.vue` — 登录表单 +- `src/components/AdminPage.vue` — 用户管理页(admin 专属) + +### 修改文件 + +- `src/App.vue` — 根据登录状态切换渲染 LoginPage 或主应用 +- `src/services/booksApi.ts` — 所有请求加 `Authorization` header,401 时触发 refresh + +### useAuth composable 行为 + +- localStorage 存 `access_token` 和 `refresh_token` +- 导出:`isLoggedIn`(computed)、`user`(ref)、`login()`、`logout()` +- 提供 `authedFetch()` 封装:自动带 token,遇 401 先尝试 refresh,refresh 失败则清除 token 跳登录页 + +### AdminPage + +- 仅当 `user.role === 'admin'` 时在主界面显示入口 +- 功能:列出所有用户、创建账号(用户名+密码)、删除账号(不能删除自己) + +--- + +## 四、环境变量 + +`.env` 新增: + +``` +JWT_SECRET=<随机长字符串> +ADMIN_USERNAME=admin +ADMIN_PASSWORD=<初始密码> +``` + +--- + +## 五、依赖 + +- `jsonwebtoken` 或 Hono 自带的 `hono/jwt` — JWT 操作 +- `bcryptjs` — 密码 hash(纯 JS,兼容 Bun) + +--- + +## 六、不在本次范围内 + +- 修改密码功能 +- 邮件验证 +- 多 admin 角色权限细分