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)
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()
}
}
})
评论区