Files
fastapi_best_architecture/backend/common/security/rbac.py

91 lines
3.0 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from fastapi import Depends, Request
from backend.common.enums import MethodType, StatusType
from backend.common.exception import errors
from backend.common.log import log
from backend.common.security.jwt import DependsJwtAuth
from backend.core.conf import settings
from backend.utils.import_parse import import_module_cached
async def rbac_verify(request: Request, _token: str = DependsJwtAuth) -> None:
"""
RBAC 权限校验(鉴权顺序很重要,谨慎修改)
:param request: FastAPI 请求对象
:param _token: JWT 令牌
:return:
"""
path = request.url.path
# API 鉴权白名单
if path in settings.TOKEN_REQUEST_PATH_EXCLUDE:
return
for pattern in settings.TOKEN_REQUEST_PATH_EXCLUDE_PATTERN:
if pattern.match(path):
return
# JWT 授权状态强制校验
if not request.auth.scopes:
raise errors.TokenError
# 超级管理员免校验
if request.user.is_superuser:
return
# 检测用户角色
user_roles = request.user.roles
if not user_roles or all(status == 0 for status in user_roles):
raise errors.AuthorizationError(msg='用户未分配角色,请联系系统管理员')
# 检测用户所属角色菜单
if not any(len(role.menus) > 0 for role in user_roles):
raise errors.AuthorizationError(msg='用户未分配菜单,请联系系统管理员')
# 检测后台管理操作权限
method = request.method
if method != MethodType.GET or method != MethodType.OPTIONS:
if not request.user.is_staff:
raise errors.AuthorizationError(msg='用户已被禁止后台管理操作,请联系系统管理员')
# RBAC 鉴权
if settings.RBAC_ROLE_MENU_MODE:
path_auth_perm = getattr(request.state, 'permission', None)
# 没有菜单操作权限标识不校验
if not path_auth_perm:
return
# 菜单鉴权白名单
if path_auth_perm in settings.RBAC_ROLE_MENU_EXCLUDE:
return
# 菜单去重
unique_menus = {}
for role in user_roles:
for menu in role.menus:
unique_menus[menu.id] = menu
# 已分配菜单权限校验
allow_perms = []
for menu in list(unique_menus.values()):
if menu.perms and menu.status == StatusType.enable:
allow_perms.extend(menu.perms.split(','))
if path_auth_perm not in allow_perms:
raise errors.AuthorizationError
else:
try:
casbin_rbac = import_module_cached('backend.plugin.casbin_rbac.rbac')
casbin_verify = getattr(casbin_rbac, 'casbin_verify')
except (ImportError, AttributeError) as e:
log.error(f'正在通过 casbin 执行 RBAC 权限校验,但此插件不存在: {e}')
raise errors.ServerError(msg='权限校验失败,请联系系统管理员')
await casbin_verify(request)
# RBAC 授权依赖注入
DependsRBAC = Depends(rbac_verify)