目 录CONTENT

文章目录

Vue动态权限菜单

cplinux98
2022-08-30 / 0 评论 / 0 点赞 / 521 阅读 / 1,399 字 / 正在检测是否收录...

00:文章简介

介绍权限控制之Django后端动态生产vue前端菜单。

01:Django代码

使用接口动态的返回vue的菜单选项,这样可以让不同权限的用户前端界面显示不同的菜单。

并且每个菜单选项的方法都带有权限认证,这样就避免了“没显示菜单,但知道URL”的用户越权访问。

1.1:生成菜单的工具类

class PermissionItem(dict):
    def __init__(self, id, itemName, path=None):
        super().__init__()
        self.id = id
        self.itemName = itemName
        self.path = path
        self.children = []

    def __setattr__(self, key, value):
        """ 将属性转换为 key-value对"""
        self[key] = value

    def __getattr__(self, key):
        return self[key]

    def append(self, child):
        self.children.append(child)

测试效果

item = PermissionItem(1, 'zhangsan', '/user')
item2 = PermissionItem(2, 'lisi', '/lisi')
print(item)
print(item.id)
print(item.append(item2))
print(item)

image-20220330140156100

1.2:返回菜单的视图类


from rest_framework.views import Request, Response
from django.contrib.auth.models import AbstractUser
from rest_framework.permissions import IsAuthenticated, IsAdminUser
from rest_framework.decorators import api_view, permission_classes, action


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def viewList(request: Request):
    user: AbstractUser = request.user
    auth = request.auth
    menulist = {
        'data': []
    }
    if user.is_superuser:  # 返回管理员菜单
        p1 = PermissionItem(101, '用户管理')
        p1.append(PermissionItem(102, '用户列表', '/users/'))
        p1.append(PermissionItem(103, '权限列表', '/users/perms/'))
        p1.append(PermissionItem(104, '角色列表', '/users/roles/'))
        menulist['data'].append(p1)

    if user.has_perm('user.view_userprofile'):  # 判断用户的权限动态返回菜单
        p9 = PermissionItem(90, '查看权限')
        p9.append(PermissionItem(91, '用户列表', '/users/'))
        p9.append(PermissionItem(92, '权限列表', '/users/perms/'))
        p9.append(PermissionItem(93, '角色列表', '/users/roles/'))
        menulist['data'].append(p9)

    if user.is_superuser:
        p2 = PermissionItem(201, '资产管理')
        p2.append(PermissionItem(202, '资产类型', '/cmdb/citypes/'))
        p2.append(PermissionItem(203, '资产列表', '/cmdb/cis/'))
        menulist['data'].append(p2)

    p10 = PermissionItem(1001, '测试项')
    p10.append(PermissionItem(1002, 't2', '/console/'))
    menulist['data'].append(p10)
    return Response(menulist)

02:Vue代码

vue会把所有的页面都集合在一起,这是vue的特性:单页应用。但我们可以对数据源接口进行权限配置,从源头上把控权限。

2.1:路由

import Dashboard from '../components/Start/Dashboard.vue'
import Discovered from '../components/Start/Discovered.vue'
import Presettings from '../components/Devices/Presettings.vue'
import Prelist from '../components/Devices/Prelist.vue'
import Installing from '../components/Devices/Installing.vue'
import Installed from '../components/Devices/Installed.vue'
import Tasktemps from '../components/Templates/Tasktemps.vue'
import Images from '../components/Templates/Images.vue'
import Ostemps from '../components/Templates/Ostemps.vue'
import Hwtemps from '../components/Templates/Hwtemps.vue'

Vue.use(VueRouter)

const routes = [
  { path: '/', redirect: '/home' },
  { path: '/login', component: Login },
  {
    path: '/home',
    component: Home,
    redirect: '/dashboard',
    children: [
      { path: '/dashboard', component: Dashboard },
      { path: '/discovered', component: Discovered },
      { path: '/presettings', component: Presettings },
      { path: '/prelist', component: Prelist },
      { path: '/installing', component: Installing },
      { path: '/installed', component: Installed },
      { path: '/tasktemps', component: Tasktemps },
      { path: '/images', component: Images },
      { path: '/ostemps', component: Ostemps },
      { path: '/hwtemps', component: Hwtemps }

    ]
  }
]

const router = new VueRouter({
  routes
})

export default router

2.2:Home组件中的菜单和方法

<template>
  <el-container>
    <el-header>
    </el-header>
    <el-container>
      <!-- 侧边栏配置 -->
      <el-aside :width="isCollapse ? '64px' : '200px'">
        <el-menu
          :collapse="isCollapse"
          :collapse-transition="false"
          background-color="#123"
          text-color="#fff"
          active-text-color="#1890ff"
          :router="true"
          :default-active="this.$route.path"
        >
          <el-menu-item index="/welcome">
            <i class="el-icon-s-home"></i>
            <span slot="title">主页</span>
          </el-menu-item>
          <!-- 动态生成的菜单栏--开始 -->
          <el-submenu v-for="item in menulist" :index="item.id + ''" :key="item.id">
            <template slot="title"
              ><i class="el-icon-user-solid"></i><span>{{ item.itemName }}</span></template
            >
            <el-menu-item v-for="subItem in item.children" :index="subItem.path" :key="subItem.id">
              <template slot="title">
                <span slot="title"> {{ subItem.itemName }}</span>
              </template>
            </el-menu-item>
          </el-submenu>
          <!-- 动态生成的菜单栏-结束 -->
        </el-menu>
      </el-aside>
      <!-- 主页部分配置 -->
      <el-main>
        <!-- 主页内部使用嵌套路由切换页面的显示 -->
        <router-view></router-view>
      </el-main>
    </el-container>
  </el-container>
</template>

<script>
export default {
  // 创建页面时的回调函数
  created() {
    this.getMenuList()
    this.getUserInfo()
  },
  // 双向绑定的数据
  data() {
    return {
      menulist: [],
      isCollapse: false,
    }
  },
  // 事件处理函数
  methods: {
    // 动态获取侧边栏菜单
    async getMenuList() {
      const { data: response } = await this.$http.get('users/menulist/')
      // 如果返回的code码有问题,那么就直接return message弹窗
      if (response.code) {
        return this.$message.error(response.message)
      }
      // 如果没有问题,那么将response.data => menulist
      this.menulist = response.data
      console.log(this.menulist)
    },
  }
}
</script>

<style lang="less" scoped>
</style>

03:Vue其他权限和安全配置

可以对vue前端设置每次访问都携带token,没有token则返回登陆界面。也可以在前端设置定时清除token(此处未实现)。

src/main.js

// 请求拦截器,只要发送请求都要带上这个token
axios.interceptors.request.use(config => {
  config.headers.Authorization = 'Bearer ' + window.localStorage.getItem('token')
  return config
})

router/index.js

// 没有token(第一次登陆/登陆失效)将重定向到/login
router.beforeEach((to, from, next) => {
  // console.log(to) // 去哪里
  // console.log(from) // 从哪里来
  // console.log(next) // 勾子函数,如果成功了就执行该函数
  if (to.path === '/login') {
    next() // 放行的意思
  } else {
    const token = window.localStorage.getItem('token')
    if (!token) {
      next('/login')
    } else {
      next()
    }
  }
})
0

评论区