BookSystem/backend/auth/utils/captcha.py
jayhgq b9150c3e29 fix(auth): 添加验证码生成异常处理
捕获验证码生成过程中的异常并抛出HTTP异常,提高系统健壮性
2026-04-28 17:51:58 +08:00

73 lines
2.4 KiB
Python
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.

from io import BytesIO
from captcha.image import ImageCaptcha
from uuid import uuid4
from utils.redis_client import get_redis, set_token_in_redis
import redis.asyncio as redis
import random
import string
from fastapi import HTTPException
def generate_captcha(length: int = 4) -> tuple[str, bytes]:
"""生成验证码图片和验证码文本"""
# 生成随机验证码
characters = string.digits + string.ascii_uppercase
captcha_text = ''.join(random.choices(characters, k=length))
try:
# 生成验证码图片
image = ImageCaptcha(width=150, height=50)
image_bytes = image.generate(captcha_text)
# 将图片转换为字节流
buffer = BytesIO()
buffer.write(image_bytes.read())
buffer.seek(0)
return captcha_text, buffer.getvalue()
except Exception as e:
raise HTTPException(status_code=500, detail="验证码生成失败")
async def save_captcha_to_redis(redis_conn: redis.Redis, captcha_id: str, captcha_text: str, expire_seconds: int = 300):
"""将验证码保存到Redis"""
await redis_conn.set(f"captcha:{captcha_id}", captcha_text, ex=expire_seconds)
async def get_captcha_from_redis(redis_conn: redis.Redis, captcha_id: str) -> str | None:
"""从Redis获取验证码"""
return await redis_conn.get(f"captcha:{captcha_id}")
async def delete_captcha_from_redis(redis_conn: redis.Redis, captcha_id: str):
"""从Redis删除验证码"""
await redis_conn.delete(f"captcha:{captcha_id}")
async def validate_captcha(redis_conn: redis.Redis, captcha_id: str, user_input: str) -> bool:
"""验证验证码是否正确"""
stored_captcha = await get_captcha_from_redis(redis_conn, captcha_id)
if not stored_captcha:
return False
# 忽略大小写比较
is_valid = stored_captcha.upper() == user_input.upper()
# 无论验证成功与否,都删除验证码(防止重复使用)
await delete_captcha_from_redis(redis_conn, captcha_id)
return is_valid
async def create_captcha(redis_conn: redis.Redis) -> tuple[str, bytes]:
"""创建验证码并保存到Redis"""
# 生成验证码
captcha_text, image_bytes = generate_captcha()
# 生成唯一ID
captcha_id = str(uuid4())
# 保存到Redis有效期5分钟
await save_captcha_to_redis(redis_conn, captcha_id, captcha_text, 300)
return captcha_id, image_bytes