sync from local backup

This commit is contained in:
chunyu
2026-08-05 23:59:22 +08:00
commit a59f46b894
409 changed files with 166027 additions and 0 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,514 @@
# 滑块验证码后端验证实现计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 为现有的纯前端滑块验证码组件添加后端数据验证,提高安全性,防止前端绕过攻击。
**Architecture:** 采用前后端分离架构。后端负责生成验证码数据(包括加密的缺口位置)和验证用户提交的滑动位置。前端负责UI交互和与后端API通信。使用token机制确保验证码的一次性使用。
**Tech Stack:**
- 前端:React, TypeScript, Ant Design, Axios
- 后端:Django REST Framework (假设), Python, 加密库(如hashlib, hmac)
---
## 文件结构
### 前端文件
- Modify: `src/components/SliderCaptcha/SliderCaptcha.tsx` - 修改为从后端获取验证码数据并发送验证请求
- Modify: `src/components/SliderCaptcha/captchaUtils.ts` - 移除本地生成逻辑,保留工具函数
- Modify: `src/utils/request.ts` - 添加验证码API请求方法
- Modify: `src/pages/SliderCaptchaDemo/SliderCaptchaDemo.tsx` - 更新演示页面以使用新的后端验证
### 后端文件(设计指导)
- Create: `captcha/views.py` - 验证码API视图
- Create: `captcha/serializers.py` - 数据序列化器
- Create: `captcha/urls.py` - URL路由
- Create: `captcha/utils.py` - 加密和验证工具函数
---
## Task 1: 后端API设计与实现
**Files:**
- Create: `captcha/views.py`
- Create: `captcha/serializers.py`
- Create: `captcha/urls.py`
- Create: `captcha/utils.py`
- [ ] **Step 1: 设计验证码数据生成算法**
后端需要生成验证码数据,包括:
1. 随机背景图片URL(或使用预设图片库)
2. 缺口位置(x, y坐标)
3. 加密的token(包含缺口位置信息,带签名防篡改)
4. 滑块图片(可由前端根据缺口位置生成,或后端提供)
加密方案:
- 使用HMAC-SHA256对缺口位置进行签名
- token格式:`base64(x,y):signature`
- 密钥使用Django的SECRET_KEY
```python
# captcha/utils.py
import hmac
import hashlib
import base64
import json
from django.conf import settings
def generate_captcha_token(x: int, y: int) -> str:
"""生成验证码token,包含加密的缺口位置"""
data = json.dumps({'x': x, 'y': y}).encode('utf-8')
signature = hmac.new(
settings.SECRET_KEY.encode('utf-8'),
data,
hashlib.sha256
).hexdigest()
token_data = base64.urlsafe_b64encode(data).decode('utf-8')
return f"{token_data}:{signature}"
def verify_captcha_token(token: str, x: int, y: int, tolerance: int = 5) -> bool:
"""验证token和用户提交的坐标"""
try:
token_data, signature = token.split(':')
data = base64.urlsafe_b64decode(token_data)
# 验证签名
expected_signature = hmac.new(
settings.SECRET_KEY.encode('utf-8'),
data,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(signature, expected_signature):
return False
# 解析原始坐标
original = json.loads(data)
# 验证用户坐标是否在容差范围内
return (abs(original['x'] - x) <= tolerance and
abs(original['y'] - y) <= tolerance)
except Exception:
return False
```
- [ ] **Step 2: 创建验证码API视图**
```python
# captcha/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.core.cache import cache
import random
from .utils import generate_captcha_token, verify_captcha_token
class GenerateCaptchaView(APIView):
"""生成验证码数据"""
def get(self, request):
# 生成随机缺口位置(实际应用中应根据图片尺寸调整)
x = random.randint(50, 250)
y = random.randint(20, 120)
# 生成token
token = generate_captcha_token(x, y)
# 缓存token,有效期5分钟
cache_key = f"captcha:{token}"
cache.set(cache_key, {'x': x, 'y': y}, 300)
# 返回数据(实际应用中应返回真实图片URL)
return Response({
'token': token,
'bg_image': '/static/captcha/bg1.jpg', # 示例背景图
'gap_x': x,
'gap_y': y,
'width': 320,
'height': 160,
'gap_size': 40
})
class VerifyCaptchaView(APIView):
"""验证用户提交的滑块位置"""
def post(self, request):
token = request.data.get('token')
user_x = request.data.get('x')
user_y = request.data.get('y')
if not all([token, user_x is not None, user_y is not None]):
return Response(
{'error': '缺少必要参数'},
status=status.HTTP_400_BAD_REQUEST
)
# 从缓存获取原始数据(可选,用于额外验证)
cache_key = f"captcha:{token}"
cached_data = cache.get(cache_key)
if not cached_data:
return Response(
{'error': '验证码已过期或无效'},
status=status.HTTP_400_BAD_REQUEST
)
# 验证token和坐标
if verify_captcha_token(token, int(user_x), int(user_y)):
# 验证成功,删除缓存(一次性使用)
cache.delete(cache_key)
return Response({'success': True, 'message': '验证成功'})
else:
return Response(
{'success': False, 'message': '验证失败'},
status=status.HTTP_400_BAD_REQUEST
)
```
- [ ] **Step 3: 创建URL路由**
```python
# captcha/urls.py
from django.urls import path
from .views import GenerateCaptchaView, VerifyCaptchaView
urlpatterns = [
path('generate/', GenerateCaptchaView.as_view(), name='generate-captcha'),
path('verify/', VerifyCaptchaView.as_view(), name='verify-captcha'),
]
```
- [ ] **Step 4: 在Django项目中注册路由**
在项目的`urls.py`中添加:
```python
from django.urls import path, include
urlpatterns = [
# ... 其他路由
path('api/captcha/', include('captcha.urls')),
]
```
- [ ] **Step 5: 创建序列化器(可选,用于数据验证)**
```python
# captcha/serializers.py
from rest_framework import serializers
class VerifyCaptchaSerializer(serializers.Serializer):
token = serializers.CharField(required=True)
x = serializers.IntegerField(required=True)
y = serializers.IntegerField(required=True)
```
- [ ] **Step 6: 提供示例背景图片**
在`static/captcha/`目录下放置几张背景图片,用于验证码显示。
---
## Task 2: 前端API集成
**Files:**
- Modify: `src/utils/request.ts`
- Modify: `src/components/SliderCaptcha/SliderCaptcha.tsx`
- Modify: `src/components/SliderCaptcha/captchaUtils.ts`
- [ ] **Step 1: 在request.ts中添加验证码API方法**
在`api_request`对象中添加captcha模块:
```typescript
// 在request.ts的api_request对象中添加
const captcha = {
generate: () =>
request.public.get("/api/captcha/generate/"),
verify: (data: { token: string; x: number; y: number }) =>
request.public.post("/api/captcha/verify/", data),
};
export const api_request = {
// ... 其他模块
captcha, // 添加这一行
};
```
- [ ] **Step 2: 修改SliderCaptcha组件,使用后端API**
修改`SliderCaptcha.tsx`,主要变更:
1. `loadCaptcha`函数改为从后端获取数据
2. `doVerify`函数改为发送验证请求到后端
3. 添加token状态管理
4. 处理API错误
```typescript
// SliderCaptcha.tsx 关键修改部分
const SliderCaptcha: React.FC<SliderCaptchaProps> = ({
width = 320,
height = 160,
gapSize = 40,
tolerance = 5,
onSuccess,
onFail,
onRefresh,
className = "",
style = {},
}) => {
const [status, setStatus] = useState<CaptchaStatus>("loading");
const [captchaData, setCaptchaData] = useState<CaptchaResult | null>(null);
const [sliderX, setSliderX] = useState(0);
const [message, setMessage] = useState("向右滑动完成验证");
const [token, setToken] = useState<string>(""); // 新增token状态
// ... 其他状态和ref
const loadCaptcha = useCallback(async () => {
setStatus("loading");
setSliderX(0);
setMessage("向右滑动完成验证");
try {
// 从后端获取验证码数据
const response = await api_request.captcha.generate();
const data = response.data;
// 使用后端返回的数据生成前端验证码
const captchaResult = await generateCaptcha(
data.bg_image,
data.width,
data.height,
data.gap_size
);
setCaptchaData(captchaResult);
setToken(data.token); // 保存token
setStatus("ready");
} catch (error) {
console.error("获取验证码失败:", error);
setMessage("验证码加载失败,请刷新重试");
setStatus("fail");
}
}, []);
const doVerify = useCallback(async () => {
if (!isDraggingRef.current || !captchaData || !token) return;
isDraggingRef.current = false;
setStatus("verifying");
try {
// 发送到后端验证
const response = await api_request.captcha.verify({
token,
x: sliderX,
y: captchaData.gapY // 如果后端需要Y坐标
});
if (response.data.success) {
setStatus("success");
setMessage("验证成功");
onSuccess?.();
} else {
setStatus("fail");
setMessage("验证失败,请重试");
setSliderX(0);
onFail?.();
setTimeout(() => {
handleRefresh();
}, 1000);
}
} catch (error) {
console.error("验证请求失败:", error);
setStatus("fail");
setMessage("验证失败,请重试");
setSliderX(0);
onFail?.();
setTimeout(() => {
handleRefresh();
}, 1000);
}
}, [captchaData, sliderX, token, onSuccess, onFail]);
// ... 其他代码保持不变
};
```
- [ ] **Step 3: 更新captchaUtils.ts**
移除本地生成逻辑,但保留`generateCaptcha`函数用于前端渲染。实际上,我们可以保留现有函数,因为它只是用于生成Canvas图像,不涉及安全逻辑。
但需要修改`getRandomImage`函数,因为图片现在由后端提供:
```typescript
// captchaUtils.ts 修改
/**
* 获取验证码图片(现在由后端提供,此函数可保留用于兼容)
*/
export const getRandomImage = (width: number, height: number): string => {
// 实际应用中,这个URL应该来自后端API
// 这里保留作为fallback
const seed = randomInt(1, 1000);
return `https://picsum.photos/seed/${seed}/${width}/${height}`;
};
```
- [ ] **Step 4: 更新演示页面**
修改`SliderCaptchaDemo.tsx`,展示后端验证的使用方式,并更新说明文档。
---
## Task 3: 安全增强与测试
**Files:**
- Modify: `src/components/SliderCaptcha/SliderCaptcha.tsx`
- Create: 测试用例
- [ ] **Step 1: 添加请求频率限制**
在前端添加请求间隔限制,防止暴力破解:
```typescript
// SliderCaptcha.tsx 中添加
const lastVerifyTime = useRef(0);
const VERIFY_COOLDOWN = 1000; // 1秒冷却时间
const doVerify = useCallback(async () => {
const now = Date.now();
if (now - lastVerifyTime.current < VERIFY_COOLDOWN) {
setMessage("操作过于频繁");
return;
}
lastVerifyTime.current = now;
// ... 验证逻辑
}, []);
```
- [ ] **Step 2: 添加错误处理和重试机制**
```typescript
// SliderCaptcha.tsx 中添加重试逻辑
const [retryCount, setRetryCount] = useState(0);
const MAX_RETRIES = 3;
const doVerify = useCallback(async () => {
// ... 验证逻辑
if (!response.data.success) {
if (retryCount < MAX_RETRIES) {
setRetryCount(prev => prev + 1);
setMessage(`验证失败,还有${MAX_RETRIES - retryCount}次机会`);
} else {
setMessage("验证次数已用完,请刷新页面");
setStatus("fail");
}
}
}, [retryCount]);
```
- [ ] **Step 3: 后端安全增强**
1. 添加请求频率限制(Django Ratelimit)
2. 记录验证尝试次数
3. 添加IP黑名单机制
4. 使用HTTPS传输
```python
# 后端安全增强示例
from django.core.cache import cache
from django_ratelimit.decorators import ratelimit
class VerifyCaptchaView(APIView):
@ratelimit(key='ip', rate='5/m', method='POST')
def post(self, request):
# ... 验证逻辑
pass
```
- [ ] **Step 4: 编写单元测试**
为后端API编写测试用例,确保:
1. token生成和验证的正确性
2. 过期token的处理
3. 错误坐标的验证
4. 频繁请求的限制
---
## Task 4: 部署与监控
**Files:**
- Modify: Django设置文件
- Create: 监控脚本
- [ ] **Step 1: 配置缓存后端**
确保Django使用Redis或Memcached作为缓存后端,用于存储验证码token:
```python
# settings.py
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
```
- [ ] **Step 2: 添加日志记录**
记录验证码生成和验证事件,用于监控和调试:
```python
# views.py 中添加日志
import logging
logger = logging.getLogger(__name__)
class GenerateCaptchaView(APIView):
def get(self, request):
logger.info(f"验证码生成: IP={request.META.get('REMOTE_ADDR')}")
# ... 生成逻辑
class VerifyCaptchaView(APIView):
def post(self, request):
logger.info(f"验证码验证: IP={request.META.get('REMOTE_ADDR')}, token={token[:10]}...")
# ... 验证逻辑
```
- [ ] **Step 3: 监控和告警**
设置监控指标:
1. 验证码生成频率
2. 验证成功率
3. 失败尝试次数
4. 异常请求模式
---
## 执行选项
**计划已保存到 `docs/plans/2026-06-13-slider-captcha-backend-verification.md`。两种执行方式:**
**1. Subagent-Driven(推荐)** - 我为每个Task分配一个子代理,任务间进行审查,快速迭代
**2. Inline Execution** - 在当前会话中执行任务,使用executing-plans,批量执行并设置检查点
**请选择执行方式?**
**如果选择Subagent-Driven:**
- **必需子技能:** 使用superpowers:subagent-driven-development
- 每个Task一个子代理 + 两阶段审查
**如果选择Inline Execution:**
- **必需子技能:** 使用superpowers:executing-plans
- 批量执行并设置检查点供审查
---
## 注意事项
1. **后端实现需要Django项目配合**:本计划假设后端使用Django REST Framework,需要后端开发人员配合实现。
2. **图片资源**:需要准备验证码背景图片,或使用图片生成服务。
3. **安全性考虑**:务必使用HTTPS,保护SECRET_KEY,定期轮换密钥。
4. **性能优化**:考虑使用CDN加速图片加载,缓存验证码数据。
5. **用户体验**:添加加载动画、错误提示、重试机制。