Files
zcglxt/app/api/v1/auth.py
Claude e71181f0a3 fix: 修复多个关键问题
- 修复前端路由守卫:未登录时不显示提示,直接跳转登录页
- 修复API拦截器:401错误不显示提示,直接跳转
- 增强验证码显示:图片尺寸从120x40增加到200x80
- 增大验证码字体:从28号增加到48号
- 优化验证码字符:排除易混淆的0和1
- 减少干扰线:从5条减少到3条,添加背景色优化
- 增强登录API日志:添加详细的调试日志
- 增强验证码生成和验证日志
- 优化异常处理和错误追踪

影响文件:
- src/router/index.ts
- src/api/request.ts
- app/services/auth_service.py
- app/api/v1/auth.py
- app/schemas/user.py

测试状态:
- 前端构建通过
- 后端语法检查通过
- 验证码显示效果优化完成

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-25 00:26:21 +08:00

148 lines
3.9 KiB
Python

"""
认证相关API路由
"""
from datetime import datetime
from fastapi import APIRouter, Depends, HTTPException, status, Header
from fastapi.security import HTTPBearer
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.deps import get_db, get_current_user
from app.schemas.user import (
LoginRequest,
LoginResponse,
RefreshTokenRequest,
RefreshTokenResponse,
ChangePasswordRequest,
)
from app.services.auth_service import auth_service
from app.models.user import User
from app.core.response import success_response
from app.core.config import settings
from jose import jwt
import logging
logger = logging.getLogger(__name__)
router = APIRouter()
security = HTTPBearer()
@router.post("/login", response_model=LoginResponse)
async def login(
credentials: LoginRequest,
db: AsyncSession = Depends(get_db)
):
"""
用户登录
- **username**: 用户名
- **password**: 密码
- **captcha**: 验证码
- **captcha_key**: 验证码UUID
"""
logger.info(f"登录请求 - 用户名: {credentials.username}, "
f"验证码: {credentials.captcha}, 验证码Key: {credentials.captcha_key}")
try:
result = await auth_service.login(
db=db,
username=credentials.username,
password=credentials.password,
captcha=credentials.captcha,
captcha_key=credentials.captcha_key
)
logger.info(f"登录成功 - 用户名: {credentials.username}")
return success_response(data=result)
except Exception as e:
logger.error(f"登录失败 - 用户名: {credentials.username}, 错误: {str(e)}", exc_info=True)
raise
@router.post("/refresh", response_model=RefreshTokenResponse)
async def refresh_token(
token_request: RefreshTokenRequest,
db: AsyncSession = Depends(get_db)
):
"""
刷新访问令牌
- **refresh_token**: 刷新令牌
"""
result = await auth_service.refresh_token(
db=db,
refresh_token=token_request.refresh_token
)
return success_response(data=result)
@router.post("/logout")
async def logout(
current_user: User = Depends(get_current_user),
authorization: str = Header(...),
db: AsyncSession = Depends(get_db)
):
"""
用户登出
"""
from app.utils.redis_client import redis_client
# 提取Token
token = authorization.replace("Bearer ", "")
# 获取Token剩余有效期
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
exp = payload.get("exp")
if exp:
# 计算剩余秒数
remaining_time = int(exp) - int(datetime.utcnow().timestamp())
if remaining_time > 0:
# 将Token加入黑名单
await redis_client.setex(
f"blacklist:{token}",
remaining_time,
"1"
)
except Exception as e:
logger.error(f"Token黑名单添加失败: {str(e)}")
return success_response(message="登出成功")
@router.put("/change-password")
async def change_password(
password_data: ChangePasswordRequest,
current_user: User = Depends(get_current_user),
db: AsyncSession = Depends(get_db)
):
"""
修改密码
- **old_password**: 旧密码
- **new_password**: 新密码
- **confirm_password**: 确认密码
"""
await auth_service.change_password(
db=db,
user=current_user,
old_password=password_data.old_password,
new_password=password_data.new_password
)
return success_response(message="密码修改成功")
@router.get("/captcha")
async def get_captcha():
"""
获取验证码
返回验证码图片和captcha_key
"""
captcha_data = await auth_service._generate_captcha()
return success_response(data={
"captcha_key": captcha_data["captcha_key"],
"captcha_image": captcha_data["captcha_base64"]
})