问题8 登录系统: - node:sqlite 账号存储(scrypt 哈希 + HMAC 签名 token),preset admin 改为 e2hang/evan1115 - /api/auth/register|login|me,me 返回 room 供重联 - 网关拦截 create/join 需登录(401),列表保持开放 踢人逻辑修正(问题2 延伸): - kickMove 真正释放座位(folded=false, name=null)而非锁死 - war/mill 去掉永久封禁语义 管理员越权:管理员 token 可删任意房间、踢任意房间内的人 单房间约束(用户新需求): - 同一用户同时只能在一个房间,create/join 他房返回 409 - 踢人后若房间仅剩踢人者则自动解散并释放其记录(修复 2 人局踢光对手后被困 409) - 陈旧房间记录兜底清理 UI 清理:移除"改名"按钮与"你的名字"输入框;删除房间按钮仅房主/管理员可见 docs: 新增 12-auth-kick-single-room.md 汇总本会话全部需求/根因/修复/边界测试 Co-Authored-By: Claude (CodeBuddy) <noreply@codebuddy.ai>
51 lines
3.3 KiB
Markdown
51 lines
3.3 KiB
Markdown
# 问题 8(通用):用户登录系统(名称 + 密码身份)
|
||
|
||
## 需求
|
||
“可不可以做一个用户登录,用户设置名称和密码然后以这个身份加入。”
|
||
即:用户在客户端注册/登录一个**账号(名称 + 密码)**,之后以该身份创建/加入房间。
|
||
—— 这是一个**跨房间、跨对局的持久身份**,区别于当前“每次改名只影响单个房间”的 `playerName`。
|
||
|
||
## 现状
|
||
- 当前身份仅为 `App.playerName`(`localStorage` 里的随机名,可随时改),
|
||
进入房间时作为 `playerName` 传给 lobby `joinMatch`;**无密码、无账号、无服务端校验**。
|
||
- boardgame.io 的 lobby `join` 用 `credentials`(server 生成的随机串)鉴权,与“用户密码”无关。
|
||
|
||
## 拟采用方案(轻量账号系统,复用现有 server)
|
||
|
||
### 后端(`packages/server`)
|
||
新增账号存储 + 登录/注册端点(挂在 `/api/auth/...`),因 server 是 InMemory(重启丢数据),
|
||
账号表也用内存 Map(可接受;如需持久化见下方决策点)。
|
||
```ts
|
||
// 内存表:username -> { passwordHash, salt }
|
||
const accounts = new Map<string, { hash: string; salt: string }>();
|
||
```
|
||
- `POST /api/auth/register` body `{ username, password }`:
|
||
校验用户名长度/字符;用 `crypto.scrypt` 加盐哈希;已存在则 409。
|
||
- `POST /api/auth/login` body `{ username, password }`:
|
||
校验哈希;成功返回 `{ token }`(用 HMAC/签名 JWT 或简单签名串,含 username + 过期时间)。
|
||
- (可选)`GET /api/auth/me` 用 token 回显 username。
|
||
|
||
> 说明:本项目的“身份”主要用于**进房时的名字与一致性**,不需要 OAuth 级安全。
|
||
> 哈希存储是基本卫生(不存明文密码)。token 用 `crypto` 签名即可,不必引入 jsonwebtoken 依赖。
|
||
|
||
### 前端(`apps/web` + `packages/ui`)
|
||
- 在 `App.tsx` 顶部加“登录/注册”栏(登录态存 `localStorage`:`{ username, token }`)。
|
||
- 登录后,把 `username` 作为进入房间时的 `playerName`(替换当前的随机名/手输名)。
|
||
- 房间列表 `LobbyRoomList` 的 `playerName` 改为取“已登录账号名”(未登录则退回原有输入/随机名)。
|
||
- 退出登录清除本地 token。
|
||
|
||
### 与问题 1/2 的耦合
|
||
- 登录身份 → `playerName` → 进房 `join`(lobby metadata name)。
|
||
- 改名(问题 1):登录用户是否允许改名?建议:**登录用户以账号名为准,房间内改名仅作用于本房间显示**(保持现有 rename move 语义)。
|
||
- 房主判定(问题 2):账号体系与“房主”是两回事;房主仍是房间创建者(metadata `createdBy`),与登录名无关。
|
||
|
||
## 决策点(需用户确认)
|
||
1. **账号持久化**:内存(重启即丢,与现有 InMemory 房间一致)vs 落盘(FlatFile/JSON)。建议先内存,后续可加。
|
||
2. **安全强度**:休闲局域网场景,scrypt + 签名 token 是否足够?还是用户期望真正的会话/多端?
|
||
3. **是否强制登录**:允许匿名(随机名)进入,还是必须登录才能创建/加入房间?建议**允许匿名**,登录为可选增强。
|
||
|
||
## 验证
|
||
- 注册 → 登录拿 token → 以该名进房,房间列表 pill 显示账号名。
|
||
- 错误密码登录失败;重复注册 409。
|
||
- 登录态在刷新后保留(localStorage)。
|