一、需求分析与技术选型
核心需求 :实现基于手机号的短信验证码登录功能,包含验证码发送、校验、用户状态管理等模块。
技术选型:
- FastAPI:高性能异步框架,内置OpenAPI文档生成
- 阿里云DysmsAPI:企业级短信服务,支持海量并发
- Redis:缓存验证码及临时会话状态
- Pydantic:数据模型定义与参数校验
- Celery(可选):异步任务队列,解耦短信发送流程
二、架构设计
分层架构:
- 路由层:处理HTTP请求/响应
- 服务层:业务逻辑与第三方服务集成
- 数据层:Redis缓存操作与数据库持久化
数据流设计:
sequenceDiagram
Client->>+API: 发送验证码请求
API->>Redis: 生成并缓存验证码(60s TTL)
API->>Celery: 异步触发短信发送
Celery->>阿里云API: 调用SendSms接口
Client->>+API: 提交手机号+验证码
API->>Redis: 校验验证码有效性
API->>DB: 创建/更新用户状态
三、关键实现步骤
步骤1:配置阿里云短信服务
- 创建企业级AccessKey(RAM子账号模式)
- 申请短信签名与模板(需阿里云人工审核)
- 安装SDK:
bash
pip install aliyun-python-sdk-core aliyun-python-sdk-dysmsapi
步骤2:定义核心数据模型
python
from pydantic import BaseModel, Field
import re
class SmsSendRequest(BaseModel):
phone: str = Field(..., regex=r'^1[3-9]\d{9}$')
class SmsLoginRequest(SmsSendRequest):
code: str = Field(..., min_length=4, max_length=6)
步骤3:实现短信服务抽象层
python
from aliyunsdkcore.client import AcsClient
from aliyunsdkdysmsapi.request.v20170525 import SendSmsRequest
class SmsService:
def __init__(self, key_id, key_secret):
self.client = AcsClient(key_id, key_secret, 'cn-hangzhou')
async def send_code(self, phone, code):
request = SendSmsRequest()
request.set_PhoneNumbers(phone)
request.set_SignName("YourSign")
request.set_TemplateCode("SMS_123456")
request.set_TemplateParam(f'{{"code":"{code}"}}')
return await self.client.do_action_with_exception(request)
步骤4:构建验证码管理模块
python
from redis import asyncio as aioredis
class CodeManager:
def __init__(self, redis_url):
self.redis = aioredis.from_url(redis_url)
async def generate_code(self, phone):
code = f"{random.randint(0,999999):06}"
await self.redis.setex(f"sms:{phone}", 300, code)
return code
async def validate_code(self, phone, code):
stored_code = await self.redis.get(f"sms:{phone}")
return stored_code == code
步骤5:实现核心路由逻辑
python
from fastapi import APIRouter, Depends, HTTPException
router = APIRouter()
@router.post("/sms/send")
async def send_sms(req: SmsSendRequest,
sms: SmsService = Depends(),
mgr: CodeManager = Depends()):
code = await mgr.generate_code(req.phone)
await sms.send_code(req.phone, code)
return {"status": "sent"}
@router.post("/sms/login")
async def sms_login(req: SmsLoginRequest,
mgr: CodeManager = Depends(),
db: Session = Depends(get_db)):
if not await mgr.validate_code(req.phone, req.code):
raise HTTPException(403, "验证码错误")
user = await db.get_user_by_phone(req.phone)
if not user:
user = await db.create_user(req.phone)
return generate_jwt(user)
四、安全加固方案
1. 防短信轰炸策略
python
# 在CodeManager中添加频率控制
async def check_send_frequency(self, phone):
key = f"counter:{phone}"
count = await self.redis.incr(key)
if count == 1:
await self.redis.expire(key, 3600)
return count <= 5 # 限制每小时5次
2. JWT令牌生成
python
from jose import jwt
from datetime import datetime, timedelta
def generate_jwt(user):
payload = {
"sub": user.id,
"phone": user.phone,
"exp": datetime.utcnow() + timedelta(days=7)
}
return jwt.encode(payload, SECRET_KEY, algorithm="HS256")
五、性能优化实践
1. 异步化改造
- 使用
async/await封装阿里云SDK调用 - Redis客户端选择
aioredis异步驱动
2. 连接池管理
python
# Redis连接池初始化
async def get_redis():
return aioredis.ConnectionPool.from_url(REDIS_URL)
3. 服务降级方案
- 短信服务不可用时切换本地日志记录
- 验证码生成添加本地缓存备份
六、部署与监控
1. 生产部署方案
bash
uvicorn main:app --workers 4 --proxy-headers --timeout-keep-alive 65
2. 监控指标
- 短信发送成功率(阿里云控制台)
- 接口平均响应时间(Prometheus + Grafana)
- 验证码校验失败率(自定义指标)
七、注意事项
- 签名模板合规性:需通过阿里云企业认证与内容审核
- 密钥安全管理:AccessKey需通过Vault或KMS加密存储
- 异常重试机制:网络波动时自动重试短信发送
- 多地域部署:根据用户位置选择最近的阿里云Region
通过本方案可实现:
- 每秒处理1000+短信验证请求
- 端到端延迟低于300ms(P99)
- 99.99%的服务可用性保障
完整实现需结合具体业务场景调整安全策略与性能参数,建议通过压力测试验证系统边界。