txw/docs/superpowers/specs/2026-04-15-login-captcha-design.md
liulujian 880006db5a docs: 添加登录验证码重构设计方案
将滑块验证改为数字+字母图形验证码,保持后端校验流程兼容。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-15 15:22:47 +08:00

222 lines
5.5 KiB
Markdown
Raw 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.

# 登录验证码重构设计方案
## 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
<t-form-item name="captchaCode">
<div class="captcha-container">
<img :src="captchaImage" @click="refreshCaptcha" class="captcha-img" />
<t-input
v-model="loginForm.captchaCode"
placeholder="请输入验证码"
maxlength="4"
style="width: 120px"
/>
<span class="refresh-btn" @click="refreshCaptcha">刷新</span>
</div>
</t-form-item>
```
### 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. 滑块相关代码注释后,页面功能不受影响