Django Vue实现动态菜单和动态权限

目录
  • 用户与用户组的架构设计
  • 动态菜单和权限的设计思路与实现
  • Vue 端如何实现动态路由
  • Django 端如何实现动态权限

随着前后端分离架构的流行,在 web 应用中,RESTful API 几乎已经成为了开发者主要选择,它使得客户端和服务端不需要保存对方的详细信息,也就是无状态性,但是这样在项目中需要动态菜单和动态权限就困难起来,本场Chat就是为大家提供一种思路来解决实际项目中如何实现动态菜单和权限。

因为 RESTful API 通常是无状态性,服务器怎么样才能知道用户已经登录呢?这个时候常用的做法就是每个请求都会携带一个 access token 来在服务端认证用户。最常用的就是 JWT 了,有感兴趣的小伙伴可以再做深入学习。通俗来讲,使用了 JWT 用户在每次请求时都会在请求头中携带一个 Token ,服务端会在执行操作之前先解析这个 Token 进行认证,认证完成之后服务端就会知道过来请求的用户详情,从而做出需要的返回。

用户与用户组的架构设计

通常在一个 web 应用设计中,首先都是从用户、用户组开始的。用户就是 web 应用的核心,JWT 认证也是因为用户才存在的。用户在使用 MySQL 的 web 应用中就是一张用户表,每一个用户就是用户表中的一条数据。用户组就相当于是用户的权限了,例如 一般的系统中都会有超级管理员、管理员、普通用户等用户,用户就是这些用户组下的集合,那么用户组和用户就是一对多的关系,或者说每个用户都有一个外键指向某个用户组 ID。当某个用户是管理员时就表示他拥有管理员用户组下的所有权限。

在这样用户组-用户的架构设计下,如何设计权限和菜单呢?首先,菜单就类似是用户操作的一些功能的集合,集合内的每个元素就相当于是权限了。例如有个菜单名为 员工管理 ,在它下面就存在四个基本权限:查看员工、新增员工、编辑员工、删除员工。也就是说把这四个权限想象成四个方法或功能,这些功能是关联某个用户组还是某个用户呢?显然是关联某个用户组是比较好的选择。因为这样用户组可以携带着很多菜单以及菜单的权限,当多个用户属于这个用户组时,这些用户就拥有了该用户组下的所有权限。否则关联某个用户的话,每个用户在新增的时候都需要设置菜单和权限的话,不仅浪费时间,还浪费资源 占有数据库空间。

用 RESTful API 的做法就是:用户组的菜单和权限会作为记录存放在权限表中,当需要的时候服务端会将这些数据转成 Json 返回给客户端使用,当然在服务端需要使用权限的接口也会使用权限表中的数据来判断,请求接口的用户是否有权限操作,根据权限表数据做不同处理。

动态菜单和权限的设计思路与实现

那么动态菜单和权限在服务端和客户端究竟怎样完成这些交互的呢?

  • 用户登录系统时输入用户名、密码等进行登录,登录成功之后会将该用户的 Token 返回给用户。
  • 用户携带着这个 Token 请求服务端的一个获取用户详细信息接口,服务端在认证通过后就将该用户的用户信息、用户组信息以及用户组的权限信息全部返回,此时客户端用户就得到了权限表的 Json 数据,根据这些数据客户端会展示不同的菜单项。
  • 客户端在收到权限 Json 数据时,根据权限中的菜单项设置,动态的设置前端菜单。做到不同的用户显示不同的菜单项。
  • 当用户在请求其他接口时,服务端会先进行用户认证,在得到当前请求用户的同时 也会得到该用户的用户组,从而得到该用户的所有权限信息。然后服务端会根据接口对应的权限详情做不同的处理,最终再返回给用户相应信息。例如 当查到该用户对当前接口只有查看权限时,如果用户发起的是 POST 请求想新增数据,那会服务端会直接返回 没有执行该操作的权限。

Vue 端如何实现动态路由

以基于 element 的管理后台为例,在 Vue 里面利用 VueX 状态管理器,当用户登录成功后,去获取用户详细信息,获取到详细信息后根据权限 Json 数据,在 Store 中将路由重新设置,不该展示路由节点的需要隐藏或删除掉。从而做到不同的用户展示不同的菜单。

Vue 端实现代码示例

import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import router from '../../router'

const user = {
  state: {
    token: getToken(),
    name: '',
    avatar: '',
    roles: [],
    // 自动权限相关
    group_id: 0,
    user_id: 0,
    menu_json: [], // 后端返回的权限Json储存在这里
    router: router // 引入路由菜单
  },

  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
    },
    SET_NAME: (state, name) => {
      state.name = name
    },
    SET_ROLES: (state, roles) => {
      state.roles = roles
    },
    SET_MENUS: (state, menus) => {
      state.menu_json = menus
    },
    SET_GROUP: (state, group_id) => {
      state.group_id = group_id
    },
    SET_USER: (state, user_id) => {
      state.user_id = user_id
    },
    SET_ROUTE: (state, router) => {
      // 动态路由、动态菜单
      console.log('router:', router.options.routes)
      var group_id = localStorage.getItem('ShopGroupId')
      var menus = JSON.parse(localStorage.getItem('ShopMenus'))
      console.log('group_id:', localStorage.getItem('ShopGroupId'))
      console.log('menus:', JSON.parse(localStorage.getItem('ShopMenus')))
      console.log('group_id为1,不执行设置router操作')
      if (group_id !== '1') {
        for (var i in router.options.routes) {
          if (router.options.routes[i].children !== undefined) {
            for (var j in router.options.routes[i].children) {
              if (router.options.routes[i].children[j].path !== 'dashboard') {
                router.options.routes[i].children[j].hidden = true
                for (var k in menus) {
                  if (menus[k].object_name == router.options.routes[i].children[j].name) {
                    if (menus[k].menu_list) {
                      router.options.routes[i].children[j].hidden = false
                    }
                  }
                }
              }
            }
          }
        }
      }
      for (var i in router.options.routes) {
        if (router.options.routes[i].children !== undefined) {
          var len_router = router.options.routes[i].children.length
          var check_len = 0
          for (var j in router.options.routes[i].children) {
            if (router.options.routes[i].children[j].path !== 'dashboard') {
              if (router.options.routes[i].children[j].hidden !== null && router.options.routes[i].children[j].hidden !== undefined && router.options.routes[i].children[j].hidden === true) {
                check_len += 1
              }
            }
          }
          if (len_router === check_len) {
            router.options.routes[i].hidden = true
          }
        }
      }
      // 动态路由、动态菜单的结束
      state.router = router
    }
  },

  actions: {
    // 登录
    Login({ commit }, userInfo) {
      return new Promise((resolve, reject) => {
        login(userInfo).then(response => {
          const data = response.data
          setToken(data.token)
          commit('SET_TOKEN', data.token)
          resolve()
        }).catch(error => {
          reject(error)
          alert(error)
        })
      })
    },

    // 获取用户信息
    GetInfo({ commit, state }) {
      return new Promise((resolve, reject) => {
        getInfo().then(response => {
          const data = response.data
          console.log(data)
          commit('SET_NAME', data.username)
          commit('SET_ROLES', [data.group.en_name])
          commit('SET_MENUS', data.group.back_menu) // 将返回的权限数据保存
          commit('SET_GROUP', data.group.id)
          commit('SET_USER', data.id)
          localStorage.setItem('ShopMenus', JSON.stringify(data.group.back_menu))
          localStorage.setItem('ShopGroupId', data.group.id)
          commit('SET_ROUTE', router)
          resolve(response)
        }).catch(error => {
          reject(error)
        })
      })
    },

    // 前端 登出
    FedLogOut({ commit }) {
      return new Promise(resolve => {
        commit('SET_TOKEN', '')
        removeToken()
        resolve()
      })
    }
  }
}

export default user

Django 端如何实现动态权限

在 Django 中利用 Permission 结合权限表,在每个接口在被调用之前先根据权限判断,调用接口的用户是否有权限操作,如果有就继续完成后面的操作,否则直返返回 无权操作 的提示语。

Django 端代码示例如下

from rest_framework import permissions
from rest_framework.permissions import DjangoModelPermissions, IsAdminUser
from rest_framework.permissions import BasePermission as BPermission
from shiyouAuth.models import GroupMenu

# 最终动态权限类
class BasePermission(object):

    def base_permission_check(self, basename):
        # 权限白名单
        if basename in ['userinfo', 'permissions', 'login', 'confdict']:
            return True
        else:
            return False

    def has_permission(self, request, view):
        print('请求的path:', request.path.split('/')[1])
        basename = request.path.split('/')[1]
        if self.base_permission_check(basename):
            return True
        admin_menu = GroupMenu.objects.filter(object_name=basename).first()
        if admin_menu is None and request.user.group.id != 1:
            return False
        if request.user.group.id == 1:
            return True
        if view.action == 'list' or view.action == 'retrieve':
            # 查看权限
            return bool(request.auth and admin_menu.menu_list == True)
        elif view.action == 'create':
            # 创建权限
            return bool(request.auth and admin_menu.menu_create == True)
        elif view.action == 'update' or view.action == 'partial_update':
            # 修改权限
            return bool(request.auth and admin_menu.menu_update == True)
        elif view.action == 'destroy':
            # 删除权限
            return bool(request.auth and admin_menu.menu_destroy == True)
        else:
            return False

    def has_object_permission(self, request, view, obj):
        basename = request.path.split('/')[1]
        if self.base_permission_check(basename):
            return True
        admin_menu = GroupMenu.objects.filter(object_name=basename).first()
        if admin_menu is None and request.user.group.id != 1:
            return False
        if request.user.group.id == 1:
            return True
        if view.action == 'list' or view.action == 'retrieve':
            # 查看权限
            return bool(request.auth and admin_menu.menu_list == True)
        elif view.action == 'create':
            # 创建权限
            return bool(request.auth and admin_menu.menu_create == True)
        elif view.action == 'update' or view.action == 'partial_update':
            # 修改权限
            return bool(request.auth and admin_menu.menu_update == True)
        elif view.action == 'destroy':
            # 删除权限
            return bool(request.auth and admin_menu.menu_destroy == True)
        else:
            return False

到此这篇关于Django Vue实现动态菜单和动态权限的文章就介绍到这了,更多相关Django Vue 动态菜单和动态权限内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Django+Vue跨域环境配置详解

    概述 在使用Django+Vue开发过程中,遇到了很多开发环境相关的问题,比如跨域,比如ajax请求参数等,本篇文章主要记录解决在开发过程中,遇到的一些问题. 跨域不带Cookie 在使用Vue脚手架开发的过程中,会使用Vue脚手架自带的Server进行项目调试,Vue自带的Server支持 hot reloading ,这个特性是非常好用的.但是在开发过程中,因为要与后端交互,所以在请求后端接口的时候,会遇到跨域问题,这个问题在一些职责划分清楚的团队并不存在,因为前端开发人员会才用Mock数据

  • Python Django Vue 项目创建过程详解

    1.创建项目 打开pycharm 终端,输入如下,创建项目 # 进入pycharm 项目目录下 cd pyWeb django-admin startproject pyweb_dome # pyweb_dome 是django项目名称 2.创建应用 # 进入项目根目录 pyweb_dome 下 cd pyweb_dome python manage.py startapp webserver # webserver 为应用名 3.创建前端项目 使用vue-cli在根目录创建一个名称叫[fron

  • Vue+Django项目部署详解

    本地项目配置 1 复制 luffy/settings/dev.py为prop.py 修改luffy/settings/prop.py中以下几项 (1) allow_hosts ALLOWED_HOSTS = [ 'api.youdomain.com', ] (2) 跨域白名单 CORS_ORIGIN_WHITELIST = ( # 前端域名 "www.youdomain.com", # 后端api接口域名 "api.youdomain.com" ) (3) 支付宝电

  • django和vue实现数据交互的方法

    我使用的是jQuery的ajax与django进行数据交互,遇到的问题是django的csrf 传输数据的方法如下: $(function() { $.ajax({ url: 'account/register', type: 'post', dataType:'json', data: $('#form1').serialize(), success: function (result) { console.log(result); if (result) { alert("result&qu

  • Django+Vue实现WebSocket连接的示例代码

    近期有一需求:前端页面点击执行任务,实时显示后端执行情况,思考一波:发现 WebSocket 最适合做这件事. 效果 测试 ping www.baidu.com 效果 点击连接建立ws连接 后端实现 所需软件包 后端主要借助Django Channels 实现socket连接,官网文档链接 这里想实现每个连接进来加入组进行广播,所以还需要引入 channels-redis . pip channels==2.2.0 channels-redis==2.4.0 引入 settings.py INS

  • django+vue实现注册登录的示例代码

    注册 前台利用vue中的axios进行传值,将获取到的账号密码以form表单的形式发送给后台. form表单的作用就是采集数据,也就是在前台页面中获取用户输入的值.numberValidateForm:前台定义的表单 $axios使用时需要在main.js中全局注册,.then代表成功后进行的操作,.catch代表失败后进行的操作 submitForm(formName) { let data = new FormData() data.append('username',this.number

  • Django vue前后端分离整合过程解析

    最近接到一个任务,就是用django后端,前段用vue,做一个普通的简单系统,我就是一搞后端的,听到vue也是比较震惊,之前压根没接触过vue. 看了vue的一些文档,还有一些项目,先说一下django与vue的完美结合吧! 首先是创建一个django项目 django-admin startproject mysite # 创建mysite项目 django-admin startapp blog # 创建blog应用 一.接下来就是安装关于vue 的东西了 1.首先安装node.js,官网地

  • Django与Vue语法的冲突问题完美解决方法

    当我们在django web框架中,使用vue的时候,会遇到语法冲突. 因为vue使用{{}},而django也使用{{}},因此会冲突. 解决办法1: 在django1.5以后,加入了标签: {% verbatim myblock %} {% endverbatim myblock %} 被此标签包裹的代码将不会被Django的模板引擎渲染. 因此,我们可以把带有{{ }} 的Vue代码放在 {% verbatim myblock %}标签中间,例如: <div id="app1&quo

  • vue+django实现下载文件的示例

    一.概述 在项目中,点击下载按钮,就可以下载文件. 传统的下载链接一般是get方式,这种链接是公开的,可以任意下载. 在实际项目,某些下载链接,是私密的.必须使用post方式,传递正确的参数,才能下载. 二.django项目 本环境使用django 3.1.5,新建项目download_demo 安装模块 pip3 install djangorestframework django-cors-headers 修改文件download_demo/settings.py INSTALLED_APP

  • Django Vue实现动态菜单和动态权限

    目录 用户与用户组的架构设计 动态菜单和权限的设计思路与实现 Vue 端如何实现动态路由 Django 端如何实现动态权限 随着前后端分离架构的流行,在 web 应用中,RESTful API 几乎已经成为了开发者主要选择,它使得客户端和服务端不需要保存对方的详细信息,也就是无状态性,但是这样在项目中需要动态菜单和动态权限就困难起来,本场Chat就是为大家提供一种思路来解决实际项目中如何实现动态菜单和权限. 因为 RESTful API 通常是无状态性,服务器怎么样才能知道用户已经登录呢?这个时

  • vue动态菜单、动态路由加载以及刷新踩坑实战

    目录 需求: 思路: 教训: 分享正文: 总结 需求: 从接口动态获取子菜单数据 动态加载 要求只有展开才加载子菜单数据 支持刷新,页面显示正常 思路: 一开始比较乱,思路很多.想了很多 首先路由和菜单共用一个全局route, 数据的传递也是通过store的route, 然后要考虑的俩个点就是一个就是渲染菜单和加载路由,可以在导航首位里处理路由,处理刷新. 还有一个地方就是菜单组件里展开事件里面 重新生成菜单数据,路由.大体思路差不多,做完就忘了..... 刷新的问题需要用本地缓存处理,之前一直

  • 详解VUE Element-UI多级菜单动态渲染的组件

    以下是组件代码: <template> <div class="navMenu"> <label v-for="navMenu in navMenus"> <el-menu-item v-if="navMenu.childs==null&&navMenu.entity&&navMenu.entity.state==='ENABLE'" :key="navMenu.

  • 基于vue 动态菜单 刷新空白问题的解决

    1.先确认自己在route.js 或者 main.js 中有没有使用 路由守卫vue.beforeEach和vue.addRouters() 促使页面每次刷新,重新根据后台返回数据生成动态路由,就像你在登陆时做的事情一样. 代码示范注意点: //注意:确定自己避免了路由守卫进入死循环 let oneRun = true; //通过oneRun变量控制 避免陷入死循环 router.beforeEach((to,from,next)=>{ if(oneRun){ oneRun = false;//

  • Vue Element前端应用开发之动态菜单和路由的关联处理

    概述 在我开发的很多系统里面,包括Winform混合框架.Bootstrap开发框架等系列产品中,我都倾向于动态配置菜单,并管理对应角色的菜单权限和页面权限,实现系统对用户权限的控制,菜单一般包括有名称.图标.顺序.URL连接等相关信息,对于VUE+Element 前端应用来说,应该原理上差不多,本篇随笔介绍结合服务端的动态菜单配置和本地路由的关联处理,实现动态菜单的维护和展示的处理. 1.菜单和路由的处理过程 由于Vue前端还需要引入路由这个概念,路由是我们前端可以访问到的对应路径集合,路由定

  • SpringBoot+Vue实现动态菜单的思路梳理

    目录 1. 整体思路 2. 前端渲染 3. 后端菜单生成 3.1 菜单表 3.2 菜单接口 关于 Spring Boot + Vue3 的动态菜单,松哥之前已经写了两篇文章了,这两篇文章主要是从代码上和大家分析动态菜单最终的实现方式,但是还是有小伙伴觉得没太看明白,感觉缺乏一个提纲挈领的思路,所以,今天松哥再整一篇文章和大家再来捋一捋这个问题,希望这篇文章能让小伙伴们彻底搞清楚这个问题. 1. 整体思路 首先我们来看整体思路. 光说思路大家还是云里雾里,我们结合具体的效果图来看: 最终菜单显示效

  • react 路由权限动态菜单方案配置react-router-auth-plus

    目录 正文 如何使用 1. 配置路由 2. 在应用的最外层渲染路由 权限说明 动态菜单 正文 在 react 中做路由权限管理,一直是比较麻烦的事,不像 vue 中有进入路由前拦截的功能.在摸鱼时间撸了一个傻瓜式配置的路由权限 library (基于 react-router v6). react-router v6 文档地址 react-router-auth-plus github 地址 如何使用 1. 配置路由 import { AuthRouterObject } from "react

  • vue+iview框架实现左侧动态菜单功能的示例代码

    最近在使用vue-cli3配合iview框架搭建新的项目中用到了iview中的menu菜单,按照官网写法固定不太好,因为一般项目都是从后端动态获取菜单列表,所以我们需要将官网代码稍作修改,代码如下: 注意事项: [1]菜单高亮部分动态绑定路由跳转的页面 Menu组件中有一个active-name反映的是当前高亮区域,因此可以动态的绑定active-name来实现高亮显示.前提是需要将MenuItem绑定的name也设置成页面路由的name [2]动态获取菜单数据,需要更新菜单 this.$nex

  • vue实现三级联动动态菜单

    本文实例为大家分享了vue实现三级联动动态菜单的具体代码,供大家参考,具体内容如下 三级联动动态菜单展示:一级菜单选中,生成二级菜单数据,二级菜单选中,生成三级菜单数据(根据上一级菜单的id,作为请求下一级菜单数据接口的参数) 1.代码 <template>   <div>     <!-- inline:代表的是行内表单,代表一行可以放置多个表单元素 -->     <el-form :inline="true" class="de

  • vue+quasar使用递归实现动态多级菜单

    本文实例为大家分享了vue+quasar使用递归实现动态多级菜单的具体代码,供大家参考,具体内容如下 效果图: 菜单初始化入口 menu.vue,初始化侧边栏菜单组建,<my-q-menu/>才是递归开始 <template>   <q-drawer     v-model="is_hide_"     show-if-above     bordered     content-class="bg-grey-2"     :width

随机推荐