dependencies.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. # -*- coding: utf-8 -*-
  2. import json
  3. from redis.asyncio.client import Redis
  4. from sqlalchemy.ext.asyncio import AsyncSession
  5. from sqlalchemy.orm import selectinload
  6. from typing import AsyncGenerator
  7. from fastapi import Depends, Request
  8. from fastapi import Depends
  9. from app.common.enums import RedisInitKeyConfig
  10. from app.core.exceptions import CustomException
  11. from app.core.database import async_db_session
  12. from app.core.redis_crud import RedisCURD
  13. from app.core.security import OAuth2Schema, decode_access_token
  14. from app.core.logger import log
  15. from app.api.v1.module_system.user.model import UserModel
  16. from app.api.v1.module_system.role.model import RoleModel
  17. from app.api.v1.module_system.user.crud import UserCRUD
  18. from app.api.v1.module_system.auth.schema import AuthSchema
  19. async def db_getter() -> AsyncGenerator[AsyncSession, None]:
  20. """获取数据库会话连接
  21. 返回:
  22. - AsyncSession: 数据库会话连接
  23. """
  24. async with async_db_session() as session:
  25. async with session.begin():
  26. yield session
  27. async def redis_getter(request: Request) -> Redis:
  28. """获取Redis连接
  29. 参数:
  30. - request (Request): 请求对象
  31. 返回:
  32. - Redis: Redis连接
  33. """
  34. return request.app.state.redis
  35. async def get_current_user(
  36. request: Request,
  37. db: AsyncSession = Depends(db_getter),
  38. redis: Redis = Depends(redis_getter),
  39. token: str = Depends(OAuth2Schema),
  40. ) -> AuthSchema:
  41. """获取当前用户
  42. 参数:
  43. - request (Request): 请求对象
  44. - db (AsyncSession): 数据库会话
  45. - redis (Redis): Redis连接
  46. - token (str): 访问令牌
  47. 返回:
  48. - AuthSchema: 认证信息模型
  49. """
  50. if not token:
  51. raise CustomException(msg="认证已失效", code=10401, status_code=401)
  52. # 处理Bearer token
  53. if token.startswith('Bearer'):
  54. token = token.split(' ')[1]
  55. payload = decode_access_token(token)
  56. if not payload or not hasattr(payload, 'is_refresh') or payload.is_refresh:
  57. raise CustomException(msg="非法凭证", code=10401, status_code=401)
  58. online_user_info = payload.sub
  59. # 从Redis中获取用户信息
  60. user_info = json.loads(online_user_info) # 确保是字典类型
  61. session_id = user_info.get("session_id")
  62. if not session_id:
  63. raise CustomException(msg="认证已失效", code=10401, status_code=401)
  64. # 检查用户是否在线
  65. online_ok = await RedisCURD(redis).exists(key=f'{RedisInitKeyConfig.ACCESS_TOKEN.key}:{session_id}')
  66. if not online_ok:
  67. raise CustomException(msg="认证已失效", code=10401, status_code=401)
  68. # 关闭数据权限过滤,避免当前用户查询被拦截
  69. auth = AuthSchema(db=db, check_data_scope=False)
  70. username = user_info.get("user_name")
  71. if not username:
  72. raise CustomException(msg="认证已失效", code=10401, status_code=401)
  73. # 获取用户信息,使用深层预加载确保RoleModel.creator被正确加载
  74. user = await UserCRUD(auth).get_by_username_crud(
  75. username=username,
  76. preload=[
  77. "dept",
  78. selectinload(UserModel.roles),
  79. "positions",
  80. "created_by"
  81. ]
  82. )
  83. if not user:
  84. raise CustomException(msg="用户不存在", code=10401, status_code=401)
  85. if not user.status:
  86. raise CustomException(msg="用户已被停用", code=10401, status_code=401)
  87. # 设置请求上下文
  88. request.scope["user_id"] = user.id
  89. request.scope["user_username"] = user.username
  90. # 过滤可用的角色和职位
  91. if hasattr(user, 'roles'):
  92. user.roles = [role for role in user.roles if role and role.status]
  93. if hasattr(user, 'positions'):
  94. user.positions = [pos for pos in user.positions if pos and pos.status]
  95. auth.user = user
  96. return auth
  97. class AuthPermission:
  98. """权限验证类"""
  99. def __init__(self, permissions: list[str] | None = None, check_data_scope: bool = True) -> None:
  100. """
  101. 初始化权限验证
  102. 参数:
  103. - permissions (list[str] | None): 权限标识列表。
  104. - check_data_scope (bool): 是否启用严格模式校验。
  105. """
  106. self.permissions = permissions or []
  107. self.check_data_scope = check_data_scope
  108. async def __call__(self, auth: AuthSchema = Depends(get_current_user)) -> AuthSchema:
  109. """
  110. 调用权限验证
  111. 参数:
  112. - auth (AuthSchema): 认证信息对象。
  113. 返回:
  114. - AuthSchema: 认证信息对象。
  115. """
  116. auth.check_data_scope = self.check_data_scope
  117. # 超级管理员直接通过
  118. if auth.user and auth.user.is_superuser:
  119. return auth
  120. # 无需验证权限
  121. if not self.permissions:
  122. return auth
  123. # 超级管理员权限标识
  124. if "*" in self.permissions or "*:*:*" in self.permissions:
  125. return auth
  126. # 检查用户是否有角色
  127. if not auth.user or not auth.user.roles:
  128. raise CustomException(msg="无权限操作", code=10403, status_code=403)
  129. # 获取用户权限集合
  130. user_permissions = {
  131. menu.permission
  132. for role in auth.user.roles
  133. for menu in role.menus
  134. if role.status and menu.permission and menu.status
  135. }
  136. # 权限验证 - 满足任一权限即可
  137. if not any(perm in user_permissions for perm in self.permissions):
  138. log.error(f"用户缺少任何所需的权限: {self.permissions}")
  139. raise CustomException(msg="无权限操作", code=10403, status_code=403)
  140. return auth