Files
huajishe-tts/docs/texas/08-user-login.md
e2hang 04d0af515c feat: 登录系统落地 + 踢人修正 + 单房间约束 + 权限收口
问题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>
2026-08-25 14:13:59 +08:00

51 lines
3.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 问题 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