mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-20 20:39:55 +00:00
refactor: 重构用户信息存储和主题管理逻辑 fix: 修正登录类型字段缺失问题 style: 统一页面导航栏样式和布局 docs: 更新README和文档内容 chore: 清理无用代码和资源文件 perf: 优化网络请求和错误处理逻辑 test: 更新API测试用例 build: 更新依赖版本和构建配置 ci: 调整CI/CD脚本配置
167 lines
5.4 KiB
Python
167 lines
5.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import json
|
|
from redis.asyncio.client import Redis
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
from typing import AsyncGenerator, Optional
|
|
from fastapi import Depends, Request
|
|
from motor.motor_asyncio import AsyncIOMotorDatabase
|
|
from fastapi import Depends
|
|
|
|
from app.core.exceptions import CustomException
|
|
from app.core.security import OAuth2Schema, decode_access_token
|
|
from app.core.database import session_connect
|
|
from app.core.logger import logger
|
|
from app.api.v1.cruds.system.user_crud import UserCRUD
|
|
from app.api.v1.schemas.system.auth_schema import AuthSchema
|
|
from app.core.redis_crud import RedisCURD
|
|
from app.common.enums import RedisInitKeyConfig
|
|
|
|
async def db_getter() -> AsyncGenerator[AsyncSession, None]:
|
|
"""获取数据库会话连接"""
|
|
async with session_connect() as session:
|
|
async with session.begin():
|
|
yield session
|
|
|
|
async def redis_getter(request: Request) -> Redis:
|
|
"""获取Redis连接"""
|
|
return request.app.state.redis
|
|
|
|
async def mongo_getter(request: Request) -> AsyncIOMotorDatabase:
|
|
"""获取MongoDB连接"""
|
|
return request.app.state.mongo
|
|
|
|
async def get_current_user(
|
|
request: Request,
|
|
token: str = Depends(OAuth2Schema),
|
|
redis: Redis = Depends(redis_getter),
|
|
db: AsyncSession = Depends(db_getter)
|
|
) -> AuthSchema:
|
|
"""
|
|
获取并验证当前用户信息
|
|
|
|
Args:
|
|
request: 请求对象
|
|
token: 认证token
|
|
db: 数据库会话
|
|
|
|
Returns:
|
|
AuthSchema: 包含用户信息的认证对象
|
|
|
|
Raises:
|
|
CustomException: 认证失败时抛出异常
|
|
"""
|
|
# 处理Bearer token
|
|
if token.startswith('Bearer'):
|
|
token = token.split(' ')[1]
|
|
|
|
# 解析token
|
|
payload = decode_access_token(token)
|
|
if not payload or not hasattr(payload, 'is_refresh') or payload.is_refresh:
|
|
raise CustomException(msg="非法凭证", code=10401, status_code=401)
|
|
|
|
online_user_info = payload.sub
|
|
# 从Redis中获取用户信息
|
|
user_info = json.loads(online_user_info) # 确保是字典类型
|
|
|
|
session_id = user_info.get("session_id")
|
|
if not session_id:
|
|
raise CustomException(msg="认证已失效", code=10401, status_code=401)
|
|
|
|
# 检查用户是否在线
|
|
online_ok = await RedisCURD(redis).exists(key=f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{session_id}')
|
|
if not online_ok:
|
|
raise CustomException(msg="认证已失效", code=10401, status_code=401)
|
|
|
|
auth = AuthSchema(db=db)
|
|
username = user_info.get("user_name")
|
|
if not username:
|
|
raise CustomException(msg="认证已失效", code=10401, status_code=401)
|
|
# 获取用户信息
|
|
user = await UserCRUD(auth).get_by_username_crud(username=username)
|
|
if not user:
|
|
raise CustomException(msg="用户不存在", code=10401, status_code=401)
|
|
if not user.status:
|
|
raise CustomException(msg="用户已被停用", code=10401, status_code=401)
|
|
|
|
# 设置请求上下文
|
|
request.scope["user_id"] = user.id
|
|
request.scope["user_username"] = user.username
|
|
|
|
# 过滤可用的角色和职位
|
|
if hasattr(user, 'roles'):
|
|
user.roles = [role for role in user.roles if role.status]
|
|
if hasattr(user, 'positions'):
|
|
user.positions = [pos for pos in user.positions if pos.status]
|
|
|
|
auth.user = user
|
|
return auth
|
|
|
|
|
|
class AuthPermission:
|
|
"""权限验证类"""
|
|
|
|
def __init__(self, permissions: Optional[list[str]] = None, check_data_scope: bool = True) -> None:
|
|
"""
|
|
初始化权限验证
|
|
|
|
Args:
|
|
permissions: 权限标识列表
|
|
check_data_scope: 是否启用严格模式校验
|
|
"""
|
|
self.permissions = set(permissions) if permissions else None
|
|
self.check_data_scope = check_data_scope
|
|
|
|
async def __call__(
|
|
self,
|
|
auth: AuthSchema = Depends(get_current_user),
|
|
) -> AuthSchema:
|
|
"""
|
|
执行权限验证
|
|
|
|
Args:
|
|
request: 请求对象
|
|
auth: 认证信息
|
|
|
|
Returns:
|
|
AuthSchema: 认证对象
|
|
|
|
Raises:
|
|
CustomException: 权限验证失败时抛出异常
|
|
"""
|
|
auth.check_data_scope = self.check_data_scope
|
|
|
|
# 超级管理员直接通过
|
|
if auth.user.is_superuser:
|
|
return auth
|
|
|
|
# 无需验证权限
|
|
if not self.permissions:
|
|
return auth
|
|
|
|
# 超级管理员权限标识
|
|
if {"*:*:*"} <= self.permissions:
|
|
return auth
|
|
|
|
# 获取用户权限集合
|
|
user_permissions = {
|
|
menu.permission
|
|
for role in auth.user.roles
|
|
for menu in role.menus
|
|
if menu.permission and menu.status
|
|
}
|
|
|
|
# 权限验证
|
|
if self.check_data_scope:
|
|
# 严格模式:要求所有权限都满足
|
|
if not all(perm in user_permissions for perm in self.permissions):
|
|
logger.error(f"用户缺少所需的权限: {self.permissions}")
|
|
raise CustomException(msg="无权限操作", code=10403, status_code=403)
|
|
else:
|
|
# 非严格模式:满足任一权限即可
|
|
if not any(perm in user_permissions for perm in self.permissions):
|
|
logger.error(f"用户缺少任何所需的权限: {self.permissions}")
|
|
raise CustomException(msg="无权限操作", code=10403, status_code=403)
|
|
|
|
return auth
|