463 lines
10 KiB
Markdown
463 lines
10 KiB
Markdown
|
|
# ZJPB v2.6.0 开发文档 - API安全优化
|
|||
|
|
|
|||
|
|
**版本号**: v2.6.0
|
|||
|
|
**开发日期**: 2026-02-06
|
|||
|
|
**功能主题**: 博查API调用优化 + 频率限制 + 安全防护
|
|||
|
|
**Git Commit**: (待提交)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📋 功能概述
|
|||
|
|
|
|||
|
|
### 问题背景
|
|||
|
|
v2.5及之前版本存在严重的成本浪费问题:
|
|||
|
|
- 每次访问详情页都自动调用博查API
|
|||
|
|
- 没有频率限制,容易被滥用
|
|||
|
|
- 缺少安全防护机制
|
|||
|
|
|
|||
|
|
### 优化目标
|
|||
|
|
1. **按需加载**: 只在用户点击按钮时才调用API
|
|||
|
|
2. **频率限制**: 每个IP每小时最多3次请求
|
|||
|
|
3. **验证码防护**: 超过阈值后需要验证码
|
|||
|
|
4. **成本控制**: 大幅降低无意义的API消耗
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 核心改进
|
|||
|
|
|
|||
|
|
### 1. 移除自动调用逻辑
|
|||
|
|
|
|||
|
|
**修改文件**: `app.py:151-240`
|
|||
|
|
|
|||
|
|
**before (v2.5)**:
|
|||
|
|
```python
|
|||
|
|
# 智能新闻更新:检查今天是否已更新过新闻
|
|||
|
|
need_update = False
|
|||
|
|
if not latest_news:
|
|||
|
|
need_update = True
|
|||
|
|
elif latest_news.created_at.date() < today:
|
|||
|
|
need_update = True
|
|||
|
|
|
|||
|
|
# 如果需要更新,自动获取最新新闻
|
|||
|
|
if need_update:
|
|||
|
|
searcher = NewsSearcher(api_key)
|
|||
|
|
news_items = searcher.search_site_news(...)
|
|||
|
|
# 保存到数据库
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**after (v2.6)**:
|
|||
|
|
```python
|
|||
|
|
# v2.6优化:移除自动调用博查API的逻辑,改为按需加载
|
|||
|
|
# 只获取数据库中已有的新闻,不再自动调用API
|
|||
|
|
|
|||
|
|
news_list = News.query.filter_by(
|
|||
|
|
site_id=site.id,
|
|||
|
|
is_active=True
|
|||
|
|
).order_by(News.published_at.desc()).limit(5).all()
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**影响**: 每次页面访问减少1次API调用,节省成本约95%+
|
|||
|
|
|
|||
|
|
### 2. 按需加载API
|
|||
|
|
|
|||
|
|
**新增路由**: `/api/fetch-news/<code>`
|
|||
|
|
**方法**: POST
|
|||
|
|
**权限**: 公开(有频率限制)
|
|||
|
|
|
|||
|
|
**功能**:
|
|||
|
|
- 用户点击"加载资讯"按钮时调用
|
|||
|
|
- 返回JSON格式的新闻列表
|
|||
|
|
- 前端动态渲染,无需刷新页面
|
|||
|
|
|
|||
|
|
### 3. 频率限制系统
|
|||
|
|
|
|||
|
|
**新增文件**: `utils/rate_limiter.py` (320行)
|
|||
|
|
|
|||
|
|
**核心类**:
|
|||
|
|
- `RateLimiter`: 基于内存的频率限制器
|
|||
|
|
- `CaptchaVerifier`: 验证码验证器
|
|||
|
|
- `get_client_ip()`: 获取真实IP(考虑代理/CDN)
|
|||
|
|
|
|||
|
|
**限制策略**:
|
|||
|
|
- 每个IP每小时最多3次请求
|
|||
|
|
- 超过限制后需要等待或完成验证码
|
|||
|
|
- 验证码要求持续30分钟
|
|||
|
|
|
|||
|
|
**配置参数**:
|
|||
|
|
```python
|
|||
|
|
# 在 fetch_news_for_site() 中配置
|
|||
|
|
limiter.is_rate_limited(
|
|||
|
|
client_ip,
|
|||
|
|
action='news_fetch',
|
|||
|
|
limit=3, # 每小时3次
|
|||
|
|
window_minutes=60 # 时间窗口60分钟
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. 验证码集成
|
|||
|
|
|
|||
|
|
**支持的服务**:
|
|||
|
|
- `simple`: 简单验证(开发测试用)
|
|||
|
|
- `recaptcha`: Google reCAPTCHA v2/v3
|
|||
|
|
- `hcaptcha`: hCaptcha
|
|||
|
|
|
|||
|
|
**集成步骤**:
|
|||
|
|
1. 在`.env`中配置密钥:
|
|||
|
|
```env
|
|||
|
|
RECAPTCHA_SECRET_KEY=your-secret-key
|
|||
|
|
# 或
|
|||
|
|
HCAPTCHA_SECRET_KEY=your-secret-key
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. 修改`app.py`中的验证器实例化:
|
|||
|
|
```python
|
|||
|
|
verifier = CaptchaVerifier(
|
|||
|
|
service='recaptcha',
|
|||
|
|
secret_key=app.config.get('RECAPTCHA_SECRET_KEY')
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. 前端添加验证码组件(见下文)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔧 技术实现
|
|||
|
|
|
|||
|
|
### 前端改进
|
|||
|
|
|
|||
|
|
**修改文件**: `templates/detail_new.html`
|
|||
|
|
|
|||
|
|
**1. 新闻区域显示逻辑**:
|
|||
|
|
```html
|
|||
|
|
<!-- 始终显示新闻区域 -->
|
|||
|
|
<div class="content-block">
|
|||
|
|
<button onclick="loadNews('{{ site.code }}')">
|
|||
|
|
{% if has_news %}获取最新资讯{% else %}加载资讯{% endif %}
|
|||
|
|
</button>
|
|||
|
|
|
|||
|
|
{% if news_list %}
|
|||
|
|
<!-- 显示已有新闻 -->
|
|||
|
|
{% else %}
|
|||
|
|
<!-- 显示占位提示 -->
|
|||
|
|
<div class="news-placeholder">
|
|||
|
|
点击右上角"加载资讯"按钮获取最新内容
|
|||
|
|
</div>
|
|||
|
|
{% endif %}
|
|||
|
|
</div>
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. AJAX加载函数**:
|
|||
|
|
```javascript
|
|||
|
|
function loadNews(siteCode) {
|
|||
|
|
fetch(`/api/fetch-news/${siteCode}`, {
|
|||
|
|
method: 'POST',
|
|||
|
|
headers: {'Content-Type': 'application/json'}
|
|||
|
|
})
|
|||
|
|
.then(response => response.json())
|
|||
|
|
.then(data => {
|
|||
|
|
if (data.success) {
|
|||
|
|
// 动态渲染新闻列表
|
|||
|
|
renderNews(data.news);
|
|||
|
|
showMessage(data.message, 'success');
|
|||
|
|
} else if (data.require_captcha) {
|
|||
|
|
// 显示验证码
|
|||
|
|
showCaptchaModal();
|
|||
|
|
} else {
|
|||
|
|
showMessage(data.error, 'error');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 后端改进
|
|||
|
|
|
|||
|
|
**修改文件**: `app.py:185-307`
|
|||
|
|
|
|||
|
|
**流程图**:
|
|||
|
|
```
|
|||
|
|
用户请求
|
|||
|
|
↓
|
|||
|
|
获取客户端IP
|
|||
|
|
↓
|
|||
|
|
检查是否需要验证码 → YES → 返回429错误
|
|||
|
|
↓ NO
|
|||
|
|
检查频率限制 → 超限 → 要求验证码 → 返回429错误
|
|||
|
|
↓ 未超限
|
|||
|
|
验证验证码(如果提供)
|
|||
|
|
↓
|
|||
|
|
记录请求
|
|||
|
|
↓
|
|||
|
|
调用博查API
|
|||
|
|
↓
|
|||
|
|
保存到数据库
|
|||
|
|
↓
|
|||
|
|
返回新闻列表
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📊 性能对比
|
|||
|
|
|
|||
|
|
### API调用次数
|
|||
|
|
|
|||
|
|
**场景**: 某个工具详情页被浏览100次
|
|||
|
|
|
|||
|
|
| 版本 | 自动调用 | 手动点击 | 总调用 | 成本 |
|
|||
|
|
|------|---------|---------|--------|------|
|
|||
|
|
| v2.5 | 100次 | 0次 | 100次 | ¥100 |
|
|||
|
|
| v2.6 | 0次 | ~10次 | 10次 | ¥10 |
|
|||
|
|
|
|||
|
|
**节省**: 约90% API成本
|
|||
|
|
|
|||
|
|
### 频率限制效果
|
|||
|
|
|
|||
|
|
**攻击场景**: 恶意脚本每秒请求1次
|
|||
|
|
|
|||
|
|
| 版本 | 1小时调用 | 成本 |
|
|||
|
|
|------|----------|------|
|
|||
|
|
| v2.5 | 3600次 | ¥3600 |
|
|||
|
|
| v2.6 | 3次 | ¥3 |
|
|||
|
|
|
|||
|
|
**防护**: 99.9% 成本节省
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🚀 部署指南
|
|||
|
|
|
|||
|
|
### 1. 更新依赖
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
pip install Flask-Limiter==3.5.0
|
|||
|
|
# 或
|
|||
|
|
pip install -r requirements.txt
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 更新代码
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git pull origin master
|
|||
|
|
# 或手动上传更新的文件
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 无需数据库迁移
|
|||
|
|
|
|||
|
|
本次更新无数据库结构变更。
|
|||
|
|
|
|||
|
|
### 4. 重启应用
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 使用1Panel或命令行
|
|||
|
|
sudo supervisorctl restart zjpb
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5. 验证部署
|
|||
|
|
|
|||
|
|
1. 访问任意工具详情页
|
|||
|
|
2. 确认不会自动加载新闻(页面加载快了)
|
|||
|
|
3. 点击"加载资讯"按钮
|
|||
|
|
4. 确认新闻正常显示
|
|||
|
|
5. 连续点击4次,确认出现频率限制提示
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ⚙️ 配置选项
|
|||
|
|
|
|||
|
|
### 频率限制参数
|
|||
|
|
|
|||
|
|
在`app.py`的`fetch_news_for_site()`函数中:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# 调整限制次数和时间窗口
|
|||
|
|
is_limited, remaining, reset_time = limiter.is_rate_limited(
|
|||
|
|
client_ip,
|
|||
|
|
action='news_fetch',
|
|||
|
|
limit=3, # 改为5次:更宽松
|
|||
|
|
window_minutes=60 # 改为30分钟:更严格
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# 调整验证码持续时间
|
|||
|
|
limiter.require_captcha(
|
|||
|
|
client_ip,
|
|||
|
|
duration_minutes=30 # 改为60分钟:更严格
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 验证码配置
|
|||
|
|
|
|||
|
|
**使用Google reCAPTCHA**:
|
|||
|
|
|
|||
|
|
1. 注册并获取密钥:https://www.google.com/recaptcha/admin
|
|||
|
|
2. 配置`.env`:
|
|||
|
|
```env
|
|||
|
|
RECAPTCHA_SITE_KEY=your-site-key
|
|||
|
|
RECAPTCHA_SECRET_KEY=your-secret-key
|
|||
|
|
```
|
|||
|
|
3. 修改`app.py`:
|
|||
|
|
```python
|
|||
|
|
verifier = CaptchaVerifier(
|
|||
|
|
service='recaptcha',
|
|||
|
|
secret_key=app.config.get('RECAPTCHA_SECRET_KEY')
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**使用hCaptcha**(国内推荐):
|
|||
|
|
|
|||
|
|
1. 注册:https://www.hcaptcha.com/
|
|||
|
|
2. 配置`.env`:
|
|||
|
|
```env
|
|||
|
|
HCAPTCHA_SITE_KEY=your-site-key
|
|||
|
|
HCAPTCHA_SECRET_KEY=your-secret-key
|
|||
|
|
```
|
|||
|
|
3. 修改`app.py`:
|
|||
|
|
```python
|
|||
|
|
verifier = CaptchaVerifier(
|
|||
|
|
service='hcaptcha',
|
|||
|
|
secret_key=app.config.get('HCAPTCHA_SECRET_KEY')
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔐 安全特性
|
|||
|
|
|
|||
|
|
### IP识别策略
|
|||
|
|
|
|||
|
|
支持CDN/代理场景,按优先级获取真实IP:
|
|||
|
|
|
|||
|
|
1. `X-Forwarded-For` 头(第一个IP)
|
|||
|
|
2. `X-Real-IP` 头
|
|||
|
|
3. `request.remote_addr`
|
|||
|
|
|
|||
|
|
### 防绕过机制
|
|||
|
|
|
|||
|
|
- 基于IP地址限制(不依赖Cookie/Session)
|
|||
|
|
- 验证码要求持续30分钟(不能通过清除缓存绕过)
|
|||
|
|
- 时间窗口滑动(不是固定时段)
|
|||
|
|
|
|||
|
|
### 日志记录
|
|||
|
|
|
|||
|
|
建议添加日志记录(TODO):
|
|||
|
|
```python
|
|||
|
|
# 记录频率限制触发
|
|||
|
|
app.logger.warning(f"Rate limit triggered: {client_ip}")
|
|||
|
|
|
|||
|
|
# 记录验证码验证失败
|
|||
|
|
app.logger.warning(f"Captcha failed: {client_ip}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📝 文档整合
|
|||
|
|
|
|||
|
|
**新增目录结构**:
|
|||
|
|
```
|
|||
|
|
docs/
|
|||
|
|
├── README.md # 文档索引
|
|||
|
|
├── deployment/ # 部署相关文档
|
|||
|
|
│ ├── DEPLOYMENT.md
|
|||
|
|
│ ├── QUICK_DEPLOY.md
|
|||
|
|
│ └── ...
|
|||
|
|
└── archive/ # 历史版本文档
|
|||
|
|
├── DEPLOY_v2.4.0.md
|
|||
|
|
├── DEVELOP_v2.4.1_TAB_FEATURE.md
|
|||
|
|
├── NEWS_FEATURE_v2.2.md
|
|||
|
|
└── WORK_PROGRESS_20250130.md
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**整合原因**: 简化根目录,提高可维护性
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🐛 已知问题和注意事项
|
|||
|
|
|
|||
|
|
### 1. 内存存储限制
|
|||
|
|
|
|||
|
|
当前使用内存存储频率限制数据,重启应用后清空。
|
|||
|
|
|
|||
|
|
**生产环境建议**:
|
|||
|
|
- 使用Redis存储(持久化)
|
|||
|
|
- 使用Flask-Limiter扩展(自带Redis支持)
|
|||
|
|
|
|||
|
|
**Redis集成示例**:
|
|||
|
|
```python
|
|||
|
|
from flask_limiter import Limiter
|
|||
|
|
from flask_limiter.util import get_remote_address
|
|||
|
|
|
|||
|
|
limiter = Limiter(
|
|||
|
|
app,
|
|||
|
|
key_func=get_remote_address,
|
|||
|
|
storage_uri="redis://localhost:6379"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@app.route('/api/fetch-news/<code>')
|
|||
|
|
@limiter.limit("3 per hour")
|
|||
|
|
def fetch_news_for_site(code):
|
|||
|
|
...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. 验证码体验
|
|||
|
|
|
|||
|
|
当前简单验证码仅用于开发测试,生产环境必须配置实际验证码服务。
|
|||
|
|
|
|||
|
|
### 3. CDN缓存
|
|||
|
|
|
|||
|
|
如果使用CDN,确保`/api/fetch-news/*`路径不被缓存:
|
|||
|
|
- Cloudflare: Page Rules设置`Cache Level: Bypass`
|
|||
|
|
- 阿里云CDN: 配置缓存规则,排除API路径
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 未来改进
|
|||
|
|
|
|||
|
|
### 短期 (1周内)
|
|||
|
|
|
|||
|
|
- [ ] 配置Redis存储替换内存存储
|
|||
|
|
- [ ] 配置生产环境验证码服务(reCAPTCHA或hCaptcha)
|
|||
|
|
- [ ] 添加请求日志记录和监控
|
|||
|
|
- [ ] 优化前端错误提示UI
|
|||
|
|
|
|||
|
|
### 中期 (1个月)
|
|||
|
|
|
|||
|
|
- [ ] 实现验证码UI组件
|
|||
|
|
- [ ] 添加管理后台查看API调用统计
|
|||
|
|
- [ ] 实现白名单机制(管理员IP不限制)
|
|||
|
|
- [ ] 添加用户友好的限流提示页面
|
|||
|
|
|
|||
|
|
### 长期 (3个月+)
|
|||
|
|
|
|||
|
|
- [ ] 实现基于用户账号的限流(登录用户更高额度)
|
|||
|
|
- [ ] API调用成本统计和预警
|
|||
|
|
- [ ] 智能频率调整(基于历史行为)
|
|||
|
|
- [ ] 分布式限流支持
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📞 技术支持
|
|||
|
|
|
|||
|
|
- **开发者**: Claude Code
|
|||
|
|
- **版本**: v2.6.0
|
|||
|
|
- **发布日期**: 2026-02-06
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ✅ 部署检查清单
|
|||
|
|
|
|||
|
|
- [ ] 已安装Flask-Limiter依赖
|
|||
|
|
- [ ] 已更新app.py代码
|
|||
|
|
- [ ] 已更新detail_new.html模板
|
|||
|
|
- [ ] 已添加utils/rate_limiter.py
|
|||
|
|
- [ ] 已更新requirements.txt
|
|||
|
|
- [ ] 应用重启成功
|
|||
|
|
- [ ] 详情页不再自动加载新闻
|
|||
|
|
- [ ] 点击按钮可以加载新闻
|
|||
|
|
- [ ] 连续请求触发频率限制
|
|||
|
|
- [ ] 错误提示正常显示
|
|||
|
|
- [ ] (可选)已配置验证码服务
|
|||
|
|
- [ ] (可选)已配置Redis存储
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**祝部署顺利!v2.6将大幅降低API成本。** 🎉
|
|||
|
|
|
|||
|
|
*最后更新: 2026-02-06*
|