feat(auth): 实现登录限流和性能优化

性能优化:
- 添加数据库索引优化查询性能 (006)
  - weather_data: user_id, date, is_favorite 索引
  - users: openid 索引
  - payment_orders: status 索引
- 新增 refresh_tokens 表支持双 Token 机制 (004)
- 新增 web_login_codes 表支持网页端扫码登录 (005)

安全增强:
- 实现基于 IP 的登录限流 (rate_limiter.rs)
  - 滑动窗口算法: 5次/分钟/IP
  - 自动清理过期记录
  - 429 TooManyRequests 响应

新模块:
- src/rate_limiter.rs: 限流模块
- src/alipay.rs: 支付宝签名模块 (RSA2)
- src/error.rs: 统一错误类型 (含 TooManyRequests)
- src/handlers/meta.rs: 元数据处理器

代码清理:
- 修复 .gitignore 规则,正确跟踪 src/ 和 migrations/
This commit is contained in:
2026-04-24 16:13:49 +08:00
parent b3dc966576
commit e6048ea010
11 changed files with 852 additions and 9 deletions

View File

@@ -1,4 +1,4 @@
use actix_web::{web, HttpResponse, Responder, post, get};
use actix_web::{web, HttpResponse, HttpRequest, Responder, post, get};
use chrono::{Utc, Duration};
use serde::{Deserialize, Serialize};
use reqwest::Client;
@@ -13,6 +13,8 @@ use crate::models::{
AppState, LoginResponse, RefreshTokenRequest, TokenRefreshResponse,
WeChatApiResponse, WeChatLoginRequest,
};
use crate::rate_limiter::LOGIN_RATE_LIMITER;
use std::sync::Arc;
#[post("/api/login")]
pub async fn login(
@@ -20,7 +22,27 @@ pub async fn login(
req: web::Json<WeChatLoginRequest>,
http_client: web::Data<Client>,
app_state: web::Data<AppState>,
http_req: HttpRequest,
) -> impl Responder {
let client_ip = http_req
.headers()
.get("X-Forwarded-For")
.and_then(|v| v.to_str().ok())
.map(|s| s.split(',').next().unwrap_or(s).trim().to_string())
.or_else(|| {
http_req
.headers()
.get("X-Real-IP")
.and_then(|v| v.to_str().ok())
.map(|s| s.to_string())
})
.unwrap_or_else(|| "unknown".to_string());
if let Err(e) = LOGIN_RATE_LIMITER.check_rate_limit(&client_ip).await {
warn!("登录请求被限流: client_ip={}", client_ip);
return HttpResponse::TooManyRequests().json(ErrorResponse::<()>::error(e.to_string()));
}
let url = format!(
"https://api.weixin.qq.com/sns/jscode2session?appid={}&secret={}&js_code={}&grant_type=authorization_code",
app_state.wechat_appid, app_state.wechat_secret, req.code