diff --git a/docs/superpowers/specs/2026-04-15-login-captcha-design.md b/docs/superpowers/specs/2026-04-15-login-captcha-design.md new file mode 100644 index 0000000..7fe8c84 --- /dev/null +++ b/docs/superpowers/specs/2026-04-15-login-captcha-design.md @@ -0,0 +1,221 @@ +# 登录验证码重构设计方案 + +## 1. 背景与目标 + +### 现状 +当前登录使用滑块拖动验证,用户体验不够友好,且代码实现较为复杂(纯前端拖动逻辑 + 后端 token 校验)。 + +### 目标 +将滑块验证改为图形数字+字母验证码,提升用户体验,简化前端逻辑,保持后端安全校验流程不变。 + +### 范围 +- 前端:`txw-mhzc-web` 登录页面(passwordlogin.vue、phonelogin.vue) +- 后端:`txw-sso` 服务新增验证码生成接口 + +--- + +## 2. 验证码规格 + +| 项目 | 规格 | +|------|------| +| 字符集 | 数字 0-9 + 大小写字母 A-Z/a-z | +| 长度 | 4 位 | +| 样式 | 扭曲变形文字 + 干扰线/噪点 | +| 图片尺寸 | 120 x 40 px | +| 格式 | Base64 PNG | +| 有效期 | Redis 存储,TTL 5 分钟 | +| 校验 | uuid + 验证码内容联合校验 | + +--- + +## 3. 数据流 + +### 验证码获取流程 +``` +前端页面加载 + → POST /sso/verify/captcha + → 后端生成 uuid + 验证码内容 + → 存入 Redis: captcha:{uuid} = 验证码内容, TTL 5min + → 返回 { uuid, imageBase64 } + → 前端渲染图片 +``` + +### 登录流程 +``` +用户输入账号密码 + 验证码 + → POST /sso/auth/login + { + username: "xxx", + password: "xxx", + captchaVerification: "uuid", + captchaCode: "Kp7m" + } + → 后端校验 captcha:{uuid} == captchaCode + → 校验通过后继续账号密码验证 +``` + +--- + +## 4. 前端改动 + +### 4.1 文件结构 + +| 文件 | 操作 | 说明 | +|------|------|------| +| `passwordlogin.vue` | 修改 | 注释滑块代码,新增验证码组件 | +| `phonelogin.vue` | 修改 | 同上 | +| `login.js` | 修改 | 新增 `getCaptcha()` API | + +### 4.2 passwordlogin.vue 改动 + +**注释掉的代码(原滑块相关):** +- `mouseDown`, `mouseMoveFn`, `moseUpFn`, `successFunction` 方法 → 注释 +- `maxWidth`, `beginClientX`, `mouseMoveState`, `confirmWords`, `confirmSuccess` 数据 → 注释 +- `.drag` 模板区域 → 注释,替换为新验证码区域 +- `mounted()` 中滑块初始化逻辑 → 注释 +- `beforeDestroy()` 中的清理逻辑 → 注释 + +**新增代码:** +- 新增 `captchaCode` 表单字段 +- 新增 `captchaUuid` 存储当前验证码 uuid +- 新增 `captchaImage` 存储验证码图片 base64 +- 新增 `getCaptcha()` 方法调用后端接口 +- 新增 `refreshCaptcha()` 刷新验证码方法 +- 模板新增验证码图片 + 输入框 + 刷新按钮区域 + +### 4.3 验证码组件 UI(新增模板区域) + +```html + +
+ + + 刷新 +
+
+``` + +### 4.4 样式新增 + +```css +.captcha-container { + display: flex; + align-items: center; + gap: 12px; +} +.captcha-img { + width: 120px; + height: 40px; + cursor: pointer; + border-radius: 4px; +} +.refresh-btn { + color: #0052d9; + cursor: pointer; + font-size: 14px; +} +``` + +### 4.5 API 改动 + +**login.js 新增:** +```javascript +// 获取图形验证码 +export const getCaptcha = () => { + return request({ + url: '/sso/verify/captcha', + method: 'post' + }) +} +``` + +--- + +## 5. 后端改动 + +### 5.1 文件结构 + +| 文件 | 操作 | 说明 | +|------|------|------| +| `VerifyController.java` | 修改 | 新增 `/verify/captcha` 接口 | +| `VerifyService.java` | 修改 | 新增 `getCaptcha()` 接口定义 | +| `VerifyServiceImpl.java` | 修改 | 实现验证码生成(使用 Hutool 的 CaptchaUtil) | +| `AuthLoginReqVO.java` | 修改 | 新增 `captchaCode` 字段 | +| `AuthServiceImpl.java` | 修改 | login() 方法新增验证码校验逻辑 | + +### 5.2 接口定义 + +**POST /sso/verify/captcha** + +请求: +```json +{ + "remoteId": "IP+UserAgent hash" +} +``` + +响应: +```json +{ + "code": 0, + "data": { + "uuid": "abc123...", + "imageBase64": "data:image/png;base64,iVBORw0KGgo..." + } +} +``` + +### 5.3 Redis 存储结构 + +| Key | Value | TTL | +|-----|-------|-----| +| `captcha:{uuid}` | 验证码内容(如 "Kp7m") | 5 分钟 | + +### 5.4 登录接口改动 + +`AuthLoginReqVO` 新增字段: +```java +private String captchaCode; // 用户输入的验证码 +``` + +`AuthServiceImpl.login()` 改动: +```java +// 原有验证码 token 校验(滑块) +verifyService.checkVerifyToken(reqVO.getCaptchaVerification()); + +// 新增图形验证码校验 +verifyService.checkCaptcha(reqVO.getCaptchaVerification(), reqVO.getCaptchaCode()); +``` + +--- + +## 6. 安全考虑 + +1. **防暴力破解**:验证码校验失败后不暴露具体错误原因("验证码错误" vs "验证码已过期") +2. **一次性**:验证码验证成功后立即从 Redis 删除 +3. **限频**:同一 IP 请求验证码频率限制(TODO) +4. **混淆**:图形验证码使用扭曲字体 + 干扰线,防止简单 OCR + +--- + +## 7. 兼容性 + +- 原滑块相关代码**注释保留**,不删除,便于后续回滚 +- `captchaVerification` 字段保留(复用为 uuid),登录参数结构兼容 +- 后端兼容旧版滑块 token 校验逻辑(captcha check 可配置开关) + +--- + +## 8. 测试要点 + +1. 验证码图片正确渲染(扭曲字符可见) +2. 点击刷新生成新验证码 +3. 输入正确验证码 + 正确账号密码 → 登录成功 +4. 输入错误验证码 → 登录失败,提示"验证码错误" +5. 验证码过期(5分钟)→ 登录失败,提示"验证码已过期" +6. 滑块相关代码注释后,页面功能不受影响