# app/utils/decorators.py from functools import wraps from flask_jwt_extended import get_jwt, verify_jwt_in_request from flask import jsonify, g import logging def role_required(*roles): """ 自定义装饰器:检查用户角色 使用方法: @role_required('super_admin', 'finance') """ def wrapper(fn): @wraps(fn) def decorator(*args, **kwargs): claims = get_jwt() user_role = claims.get('role') user_role_upper = user_role.upper() if user_role else None # 如果是超级管理员,拥有上帝视角,直接放行 (可选) if user_role_upper == 'SUPER_ADMIN': return fn(*args, **kwargs) if user_role_upper not in [r.upper() for r in roles]: return jsonify(msg='权限不足:您没有访问此资源的权限'), 403 return fn(*args, **kwargs) return decorator return wrapper def login_required(fn): """ 验证 JWT 令牌是否存在且有效 """ @wraps(fn) def decorator(*args, **kwargs): try: verify_jwt_in_request() except Exception as e: logging.warning(f"JWT verification failed: {e}") return jsonify(msg='登录已过期,请重新登录'), 401 return fn(*args, **kwargs) return decorator def permission_required(permission_code): """ 检查当前用户是否拥有指定权限码 使用方法: @permission_required('material:base:read') """ def wrapper(fn): @wraps(fn) def decorator(*args, **kwargs): # 首先验证 JWT try: verify_jwt_in_request() except Exception as e: logging.warning(f"JWT verification failed: {e}") return jsonify(msg='登录已过期,请重新登录'), 401 claims = get_jwt() user_role = claims.get('role') # 超级管理员放行 (忽略大小写) if user_role and user_role.upper() == 'SUPER_ADMIN': return fn(*args, **kwargs) # 根据角色查询数据库中的权限 try: from app.services.auth_service import AuthService perm_dict = AuthService.get_user_permissions(user_role) except Exception as e: logging.warning(f"Failed to fetch permissions for role {user_role}: {e}") return jsonify(msg='权限查询失败'), 403 # 合并菜单和元素权限 all_perms = perm_dict.get('menus', []) + perm_dict.get('elements', []) if permission_code not in all_perms: # 详细的调试日志 print(f"🔴 [权限拦截] 角色 '{user_role}' 访问被拒!需要权限码: '{permission_code}', 但该角色实际拥有: {all_perms}") logging.warning( f"权限检查失败: 角色={user_role}, 所需权限={permission_code}, 实际权限列表={all_perms}") return jsonify(msg='权限不足:您没有访问此资源的权限'), 403 return fn(*args, **kwargs) return decorator return wrapper