# 问题 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(); ``` - `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)。