成品效果
只要django 安装了 ‘corsheaders’, settings.py文件中配置了这个跨域参数就可以,就可以让vue访问restframework 后端无任何阻碍
vue前端页面显示控制,目前所知有两种方案
第一种:通过路由导航中的meta参数设置可以访问的角色权限,后台只需要提供访问用户的角色和meta参数的角色进行遍历,在里面就可以访问这个菜单,不在就不能访问,但是一旦相对某一个已有的角色进行权限升级或者降级就需要重新编写路由导航routers,重启程序,很不方便。
第二种是:所有的前端展示路由在vue routers中部署完毕,但是里面的角色不固定,通过后端提供的请求连接地址和routers中的信息进行匹配过滤,这样的好处是如果前端页面菜单栏是固定的,用户的角色,角色的权限都可以实现动态的增删改查,而不需要对前端的程序进行打包重新整理。我才用的是第二种
流程为:前端用户登录->后端获取用户->初始化用户权限->路由过滤->显示用户可使用菜单
vue的main.js程序为所有请求页面的入口函数,会对插件,全局css,全局路由进行设置
对axios请求头和返回结果进行处理,后续使用可以直接使用配置
httpconfig.js
import axios from 'axios'
import {Message} from 'element-ui'
import router from '../router'
const http = {}
const baseURL='http://192.168.56.104:8000'
import { getToken, removeToken } from '@/utils/auth'
const service=new axios.create({
baseURL,
timeout:500000,
}
)
service.interceptors.request.use(
function(config){
const Token = getToken()
if(Token){
config.headers.Authorization=Token
}
else{
}
return config
},
function(error){
return Promise.reject(error)
}
)
service.interceptors.response.use(
response => {
const res = response.data
if(res.code===50014||res.code===50012||res.code===50008){
Message({
message: res.error,
type: 'error',
duration: 5 * 1000
})
removeToken()
return router.replace('/login')
}
return res
},
error => {
console.log('错误问题' + error)
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
uitils.auth.js的代码是:
export function getToken() {
return localStorage.getItem('token')
}
export function setToken(token) {
return localStorage.setItem('token', token)
}
export function removeToken() {
return localStorage.removeItem('token')
}
由于vue需要写的比较多,博客中展示主要功能代码,login的关键代码,输入用户名密码,向服务器后端发送post请求,django返回token值,并且返回该用户在系统中权限
mysql后端表格数据
django后端代码init_permission.py
from django.db.models import Aggregate, CharField
from django.http import HttpResponse
class Concat(Aggregate):
"""ORM用来分组显示其他字段 相当于group_concat"""
function = 'GROUP_CONCAT'
template = '%(function)s(%(distinct)s%(expressions)s)'
def __init__(self, expression, distinct=False, **extra):
super(Concat, self).__init__(
expression,
distinct='DISTINCT ' if distinct else '',
output_field=CharField(),
**extra)
def init_menu(user_obj):
try:
user=user_obj
menus = user.roles.filter(permissions__type__in=[0,1]).values('permissions__id',
'permissions__code',
'permissions__icon',
'permissions__name',
'permissions__parent',
'permissions__type',
'permissions__oprtype').distinct()
menus_all=[]
for i in menus:
menu_item={
'id':i['permissions__id'],
'code':i['permissions__code'],
'icon':i['permissions__icon'],
'name':i['permissions__name'],
'parent':i['permissions__parent'],
'type':i['permissions__type'],
'oprtype':i['permissions__oprtype'],
'children':[]}
menus_all.append(menu_item)
menutree={}
for i in menus_all:
menutree[i['id']]=i
menu_data=[]
for i in menutree:
if menutree[i]['parent']:
parent_id=menutree[i]['parent']
parent_menu = menutree[parent_id]
parent_menu['children'].append(menutree[i])
else:
menu_data.append(menutree[i])
return menu_data
except Exception as e:
err={'code':'404','identifed':'菜单获取失败'}
return err
def init_permission(user_obj):
try:
user=user_obj
menus = user.roles.values('permissions__id',
'permissions__code',
'permissions__icon',
'permissions__name',
'permissions__parent',
'permissions__type',
'permissions__oprtype').distinct()
btns=user.roles.values('permissions__code').distinct()
btnlist=[i['permissions__code'] for i in btns]
menus_all=[]
for i in menus:
menu_item={
'id':i['permissions__id'],
'code':i['permissions__code'],
'icon':i['permissions__icon'],
'name':i['permissions__name'],
'parent':i['permissions__parent'],
'type':i['permissions__type'],
'oprtype':i['permissions__oprtype'],
'children':[]}
menus_all.append(menu_item)
menutree={}
for i in menus_all:
menutree[i['id']]=i
menu_data=[]
for i in menutree:
if menutree[i]['parent']:
parent_id=menutree[i]['parent']
parent_menu = menutree[parent_id]
parent_menu['children'].append(menutree[i])
else:
menu_data.append(menutree[i])
return menu_data
except Exception as e:
err={'code':'404','identifed':'权限返回失败'}
return err
def init_btn(user_obj):
try:
user = user_obj
btns = user.roles.values('permissions__code').distinct()
btn_data = [i['permissions__code'] for i in btns]
return btn_data
except Exception as e:
err = {'code': '404', 'identifed':'按钮获取失败'}
return err
vue前端请求代码:
axios.post('/token',this.$qs.stringify({username:this.ruleForm2.username,password:this.ruleForm2.password}))
restframework中的后端脚本auth_token.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.conf import settings
from crm.models import User
from utils.init_permission import init_menu,init_permission,init_btn
import jwt
import sys
from jwt import exceptions
class ApiToken(APIView):
authentication_classes = []
permission_classes = []
def get(self, request,*args, **kwargs):
token = request.META.get('HTTP_AUTHORIZATION', '')
salt = settings.SECRET_KEY
try:
payload = jwt.decode(token, salt, True)
except exceptions.ExpiredSignatureError:
msg = 'token已失效'
return Response({'code': 50014, 'error': msg})
except jwt.DecodeError:
msg = 'token认证失败,请检查用户名密码是否正确'
return Response({'code': 50012, 'error': msg})
except jwt.InvalidTokenError:
msg = '非法的token'
return Response({'code': 50008, 'error': msg})
user_obj=User.objects.get(id=payload['user_id'])
menulist=init_menu(user_obj)
permissionlist=init_permission(user_obj)
btnlist=init_btn(user_obj)
return Response({'payload':payload, 'token':token,'menulist':menulist,'permissionlist':permissionlist,'btnlist':btnlist})
def post(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
user_object = User.objects.get(username=username)
if not user_object or not user_object.check_password(password):
return Response({'code': 401, 'error': '用户名或密码错误'})
import jwt
import datetime
salt = settings.SECRET_KEY
headers = {
'typ': 'jwt',
'alg': 'HS256'
}
payload = {
'user_id': user_object.id,
'username': user_object.username,
'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=800)
}
token = jwt.encode(payload=payload, key=salt, algorithm='HS256', headers=headers).decode('utf-8')
return Response({'code': 201, 'token': token})
url显示:
url(r'token',ApiToken.as_view())
这个里面对framework的get,post请求进行了改写,当有相关请求来到的时候,会根据请求的方式,对应不同的方案,为什么要get请求,主要是为了后续vuex持久化考虑,因为一旦网页界面刷新,vuex包括的东西如果不保存在localstorage或者sessionstorage中,刷新vuex中的东西就没了,对于一些需要使用vuex内容的组件来说是一个灾难,所以需要通过请求头的token值,/token的get方法再次获取权限和用户相关信息
vue关键代码:
卸载router.beforeeach
if(!store.state.permission.permissionList){
store.dispatch('permission/FETCH_PERMISSION').then((res) => {
next({path:to.path})
})
}
写在store文件夹下modules文件夹下的permission.js
import { fetchPermission } from "@/api/permission";
import router ,{ DynamicRoutes } from '@/router/index'
import dynamicRouter from '@/router/dynamic-router'
import { recursionRouter,setDefaultRoute } from '@/utils/recursion-router'
export default{
namespaced:true,
state:{
permissionList:'',
backpermission:'',
sidebarMenu:'',
btnlist:'',
currentMenu:'',
username:'',
userid:''
},
getters:{},
mutations:{
SET_USERID(state,value){
state.userid=value
},
SET_USERNAME(state,value){
state.username=value
},
SET_PERMISSION(state,routes){
state.permissionList=routes
},
SET_BACKPERMISSION(state,values){
state.backpermission=values
},
SET_BTNLIST(state,values){
state.btnlist=values
},
CLEAR_PERMISSION(state){
state.permissionList=null
},
SET_MENU(state,menu){
state.sidebarMenu=menu
},
CLEAR_MENU(state){
state.sidebarMenu=[]
},
SET_CURRENT_MENU(state){
state.currentMenu=''
}
},
actions:{
async FETCH_PERMISSION(context){
await fetchPermission().then(res=>{
let routes = recursionRouter(res.menulist, dynamicRouter)
let MainContainer = DynamicRoutes.find(v => v.path === '/')
let children = MainContainer.children
children.push(...routes)
context.commit('SET_MENU',children)
setDefaultRoute([MainContainer])
let initialRoutes = router.options.routes
router.addRoutes(DynamicRoutes)
context.commit('SET_PERMISSION', [...initialRoutes, ...DynamicRoutes])
context.commit('SET_BACKPERMISSION',res.permissionlist)
context.commit('SET_BTNLIST',res.btnlist)
context.commit('SET_USERNAME',res.payload.username)
context.commit('SET_USERID',res.payload.user_id)
}).catch(error=>{
console.log('执行报错了,错误为',error)
})
}
}
}
这样每次请求页面的时候如果发现vuex相关内容没有,那就直接调用程序再次加载,这个项目是我自己写的,所以没考虑redis缓存实现,使用redis的话可以减少请求次数,感兴趣的同学可以试试。
到此 vue和restframework前后端分离,登录以及权限加载部分实现完毕
- 技术无止境
Original: https://blog.csdn.net/laoli815/article/details/123126224
Author: laoli815
Title: vue结合django restframework登录和权限菜单加载
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/733741/
转载文章受原作者版权保护。转载请注明原作者出处!