初始提交: Qihu-AIFlow v2.0.1 完整项目
- 前端 React+TypeScript (源码+构建产物) - 后端 Python FastAPI - 数据库迁移 001-015 - Docker部署配置 - 微信ClawBot API频道绑定 - 项目文档 README.md
This commit is contained in:
commit
78ff261b5a
199 files changed
+69126
No files matched your search
@@ -0,0 +1,102 @@
|
||||
"""
|
||||
AIFlow 认证与授权模块
|
||||
JWT Token 生成与验证, 密码哈希
|
||||
使用 bcrypt 库直接处理密码哈希,避免 passlib 与新版 bcrypt 的兼容性问题
|
||||
"""
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
import bcrypt
|
||||
from jose import JWTError, jwt
|
||||
from fastapi import Depends, HTTPException, status, Request
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
from .config import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
security = HTTPBearer()
|
||||
security_optional = HTTPBearer(auto_error=False)
|
||||
|
||||
|
||||
def hash_password(password: str) -> str:
|
||||
"""使用 bcrypt 直接生成密码哈希"""
|
||||
return bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()).decode("utf-8")
|
||||
|
||||
|
||||
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
"""使用 bcrypt 直接验证密码,捕获所有异常确保稳定性"""
|
||||
try:
|
||||
return bcrypt.checkpw(
|
||||
plain_password.encode("utf-8"),
|
||||
hashed_password.encode("utf-8"),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"密码验证异常: hashed_password 前缀={hashed_password[:7]}... 长度={len(hashed_password)} 错误={e}")
|
||||
return False
|
||||
|
||||
|
||||
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
|
||||
to_encode = data.copy()
|
||||
expire = datetime.utcnow() + (expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES))
|
||||
to_encode.update({"exp": expire})
|
||||
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||
|
||||
|
||||
def decode_access_token(token: str) -> Optional[dict]:
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
return payload
|
||||
except JWTError:
|
||||
return None
|
||||
|
||||
|
||||
async def get_current_user_id(
|
||||
credentials: HTTPAuthorizationCredentials = Depends(security)
|
||||
) -> str:
|
||||
"""从JWT Token中提取当前用户ID"""
|
||||
payload = decode_access_token(credentials.credentials)
|
||||
if payload is None:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="无效的认证令牌")
|
||||
user_id = payload.get("sub")
|
||||
if user_id is None:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="无效的认证令牌")
|
||||
return user_id
|
||||
|
||||
|
||||
async def get_current_user_id_optional(
|
||||
credentials: Optional[HTTPAuthorizationCredentials] = Depends(security_optional),
|
||||
) -> Optional[str]:
|
||||
"""从JWT Token中提取当前用户ID,不强制要求登录"""
|
||||
if credentials is None:
|
||||
return None
|
||||
payload = decode_access_token(credentials.credentials)
|
||||
if payload is None:
|
||||
return None
|
||||
return payload.get("sub")
|
||||
|
||||
|
||||
async def get_current_user(
|
||||
user_id: str = Depends(get_current_user_id),
|
||||
):
|
||||
"""获取当前用户对象(延迟导入避免循环引用)"""
|
||||
from .database import SessionLocal
|
||||
from .models import User
|
||||
db = SessionLocal()
|
||||
try:
|
||||
user = db.query(User).filter(User.id == user_id).first()
|
||||
if not user:
|
||||
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="用户不存在")
|
||||
if user.status == "banned":
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="账号已被封禁")
|
||||
return user
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
async def get_current_admin(
|
||||
user=Depends(get_current_user),
|
||||
):
|
||||
"""验证当前用户为管理员"""
|
||||
if user.role != "admin":
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="需要管理员权限")
|
||||
return user
|
||||
Reference in new issue
Block a user