fastapi+vue实现按钮级别的权限控制

devtools/2025/2/21 18:54:21/

一、前端部分

1.1 自定义指令

import store from '@/store'// 判断是否有权限
const hasPermission = (value, el) => {// 检查是否配置了权限参数if (!Array.isArray(value) || value.length === 0) {throw new Error(`v-permission 需要配置权限,例如 v-permission="['xxx']"`)}// 获取用户权限,登录后从store中获取到const ruleNames = store.getters['permissions'] || []if (!Array.isArray(ruleNames)) {console.warn('权限数据 "menu/getRuleNames" 格式不正确,请检查 store 配置。')return}// 判断是否有权限const hasAuth = value.some((val) => ruleNames.includes(val))if (!hasAuth) {el.style.display = 'none'}return hasAuth
}export default {install(Vue) {Vue.directive('permission', {bind(el, binding) {hasPermission(binding.value, el)},updated(el, binding) {hasPermission(binding.value, el)}})}
}

1.2 注册自定义指令

import permission from '@/utils/utils'// 注册自定义指令
Vue.use(permission)

1.3 在组件中还用自定义指令

    <el-button v-permission="['add_dept']" type="primary" size="medium" @click="addDeptBtn">新增部门</el-button>

二、后端部分

整理思路为:用户登录后生成token,然后根据fastapi的oauth2编写依赖项,并将其注入到所有的路由函数中表示需要token才能进行访问,然后再在每一个接口函数中,使用依赖性注入判断权限标识的方法,判断根据token中的用户id是否存在此接口的权限标识

2.1 生成token的方法

def create_token(payload: dict, expires: timedelta = None):"""根据用户的电话号码和密码生成token:param payload: 载荷-用户的电话号码和密码:param expires: 过期时间:return: token"""if expires:expire = datetime.now() + expireselse:expire = datetime.now() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)payload.update({"exp": expire})token = jwt.encode(payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)return token

2.2 登录接口需要的一些工具方法

from datetime import datetimefrom sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession
from starlette import status
from starlette.exceptions import HTTPException
from starlette.requests import Request
from apps.vadmin.auth.models import VadminUser, VadminRole
from apps.vadmin.auth.schemas.auth import LoginSchema
from apps.vadmin.auth.schemas.role import RoleOutSchema
from apps.vadmin.record.models import VadminLoginRecord, VadminRecordAction
from apps.vadmin.record.schemas.login import LoginForm# 获取用户信息的函数
async def get_user_by_telephone(username: str, db: AsyncSession):stmt = select(VadminUser).where(VadminUser.telephone == username).filter(VadminUser.is_delete == False)return await db.scalar(stmt)# 获取用户权限
async def get_user_permissions(user: VadminUser, db: AsyncSession):permissions = []user_role_list = user.rolesfor role in user_role_list:role_stmt = select(VadminRole).where(VadminRole.id == role.id, VadminRole.is_delete == False)role_model = await db.scalar(role_stmt)if not role_model:raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail='当前用户的角色不存在!')role_info = RoleOutSchema.from_orm(role_model).model_dump()permissions.extend(menu.get('perms') for menu in role_info.get('menus', []))return permissions# 记录登录操作
async def create_login_record(data: LoginSchema, request: Request, response_data: dict, db: AsyncSession):login_form = LoginForm(telephone=data.username, password=data.password, method="0", platform="0")await VadminLoginRecord.create_login_record(db, login_form, True, request, response_data)await VadminRecordAction.create_action_record(db=db,action_type='登录操作',action_user=data.username,action_tag='登录模块',action_description='用户登录',data={'telephone': data.username},req=request,resp=response_data,method=request.method,status=True)# 更新登录时间login_stmt = update(VadminUser).where(VadminUser.telephone == data.username).values(last_login=datetime.now())await db.execute(login_stmt)

2.2 校验token并获取token中的用户信息

from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession
from starlette import status
from starlette.requests import Request
from apps.vadmin.auth.models import VadminUser
from apps.vadmin.auth.schemas.auth import LoginSchema
from apps.vadmin.auth.schemas.user import OutUsrSchema
from apps.vadmin.auth.validate.auth import get_user_by_telephone, get_user_permissions, create_login_record
from core.database import db_getter
from core.utils import create_token
from utils.response import ErrorResponseapp = APIRouter()@app.post('/login', summary="登录")
async def login(request: Request, data: LoginSchema, db: AsyncSession = Depends(db_getter)):# 1. 校验用户是否存在user = await get_user_by_telephone(data.username, db)if not user or not VadminUser.verify_password(data.password, user.password):return ErrorResponse(status_code=status.HTTP_401_UNAUTHORIZED, msg="手机号或密码错误!!!")# 2. 校验用户状态if not data.is_active:return ErrorResponse(status_code=status.HTTP_403_FORBIDDEN, msg="该用户已被禁用,请联系管理员!!!")if not data.is_staff:return ErrorResponse(status_code=status.HTTP_403_FORBIDDEN, msg="该用户无权限,请联系管理员!!!")# 3. 整理用户信息并返回permissions = await get_user_permissions(user, db)# 4. 生成tokentoken = create_token({'telephone': data.username, 'user_id': user.id})# 5. 返回用户信息和 tokenuser_info = OutUsrSchema.from_orm(user).model_dump()user_info.update({'permissions': permissions})response_data = {'user': user_info, 'token': token}response = {"code": 200, "data": response_data, "message": "登录成功!!!"}await create_login_record(data, request, response_data, db)return response

2.3 装饰器校验权限

# 权限依赖项
def check_permissions(required_roles: List):def permission_dependency(user: Dict = Depends(get_current_user)):user_permissions_list = user.get('permissions', [])for required_role in required_roles:if required_role not in user_permissions_list:raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail='您无权限操作此权限!')return userreturn permission_dependency

2.4 使用依赖注入校验接口是否有权限

@app.get('/dept', summary='获取部门列表', response_model=DeptSimpleResponse,dependencies=[Depends(check_permissions(['get_dept']))])
async def get_dept_list(params: DeptQuerySchema = Depends(), db: AsyncSession = Depends(db_getter)):dept_data_list = await get_dept_tree_or_list_curd(params, db)return SuccessResponse(data=[dept_data.model_dump() for dept_data in dept_data_list], msg='获取部门列表成功!')@app.post('/dept', summary='创建部门', response_model=DeptSimpleResponse,dependencies=[Depends(check_permissions(['add_dept']))])
async def create_dept(data: DeptCreateSchema, db: AsyncSession = Depends(db_getter)):new_dept = await create_dept_curd(data, db)return SuccessResponse(data=new_dept.model_dump(), msg='创建部门成功!')@app.put('/dept/{dept_id}', summary='更新部门信息', response_model=DeptSimpleResponse,dependencies=[Depends(check_permissions(['update_dept']))])
async def update_dept(dept_id: int, data: DeptUpdateSchema, db: AsyncSession = Depends(db_getter)):updated_dept = await update_dept_curd(dept_id, data, db)return SuccessResponse(data=updated_dept.model_dump(), msg='更新部门成功!')@app.delete('/dept/{dept_id}', summary='删除部门', response_model=DeptSimpleResponse,dependencies=[Depends(check_permissions(['delete_dept']))])
async def delete_dept(dept_id: int, db: AsyncSession = Depends(db_getter)):await delete_dept_curd(dept_id, db)return SuccessResponse(msg='删除部门成功!')


http://www.ppmy.cn/devtools/160268.html

相关文章

iOS App的启动与优化

App的启动流程 App启动分为冷启动和热启动 冷启动&#xff1a;从0开始启动App热启动&#xff1a;App已经在内存中&#xff0c;但是后台还挂着&#xff0c;再次点击图标启动App。 一般对App启动的优化都是针对冷启动。 App冷启动可分为三个阶段&#xff1a; dyld&#xff1a…

【R语言】主成分分析与因子分析

一、主成分分析 主成分分析&#xff08;Principal Component Analysis, PCA&#xff09;是一种常用的无监督数据降维技术&#xff0c;广泛应用于统计学、数据科学和机器学习等领域。它通过正交化线性变换将&#xff08;高维&#xff09;原始数据投影到一个新的坐标系&#xff…

oracle序列每天重置

在Oracle数据库中&#xff0c;若要实现序列每天重置&#xff0c;可以通过以下步骤进行操作&#xff1a; 一、创建序列 首先&#xff0c;需要创建一个序列。创建序列的SQL语句如下&#xff1a; CREATE SEQUENCE sequence_name START WITH 0 -- 或其他起始值 INCREMENT BY 1 CA…

【Vue】集成Antlr4

1.下载.g4文件 下载地址&#xff1a;https://download.csdn.net/download/qq_42454367/90396095 2.安装Antlr &#xff08;1&#xff09;使用以下命令安装依赖 pnpm install antlr4ng pnpm install --save-dev antlr4ng-cli&#xff08;2&#xff09;在package.json文件中配…

【产品资料】陀螺匠·企业助手v1.8 产品介绍

陀螺匠企业助手是一套采用Laravel 9框架结合Swoole高性能协程服务与Vue.js前端技术栈构建的新型智慧企业管理与运营系统。该系统深度融合了客户管理、项目管理、审批流程自动化以及低代码开发平台&#xff0c;旨在为企业提供一站式、数字化转型的全方位解决方案&#xff0c;助力…

调用DeepSeek API接口:实现智能数据挖掘与分析

调用DeepSeek API接口:实现智能数据挖掘与分析 在当今数据驱动的时代,企业和开发者越来越依赖高效的数据挖掘与分析工具来获取有价值的洞察。DeepSeek作为一款先进的智能数据挖掘平台,提供了强大的API接口,帮助用户轻松集成其功能到自己的应用中。本文将详细介绍如何调用D…

【前端框架】深入探讨 Vue 3 组件生命周期的变化和最佳实践

一、Vue 3 组件生命周期的变化 1. 生命周期钩子的更名与调整 在 Vue 2 中&#xff0c;组件生命周期钩子包括 beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy 和 destroyed。而在 Vue 3 中&#xff0c;部分钩子进行了更名&#xff0c;以…

深度优先搜索

1. 算法思想 DFS 通过递归或栈来实现&#xff0c;其过程如下&#xff1a; 从起始节点开始&#xff0c;访问该节点并标记为已访问。 选择一个未访问的邻接节点&#xff0c;继续深入探索。 如果当前节点没有未访问的邻接节点&#xff0c;则回溯到上一个节点。 重复上述过程&a…