权限管理

简述权限管理:


你可以在后台通过一个 tree 控件或者其它展现形式给每一个页面动态配置权限,之后将这份路由表存储到后端。当用户登录后得到roles,前端根据roles去向后端请求可访问的路由表,从而动态生成可访问页面,之后就是 router.addRoutes 动态挂载到router 上,你会发现原理是相同的,万变不离其宗。和后台开发时人员商量,能不能在登录之后,把相关权限接口数据返回给我,然后进行菜单的动态展示

前端权限的意义
1降低非法操作的可能性
2.尽可能的排除不必要的请求,减少服务器压力
3.提高用户体验

  • 前端权限控制思路
    • 菜单的控制(管理系统的侧边栏)

      在登陆请求中,会得到权限数据。当然,这个需要后台返回的数据的支持,前段根据权限数据,展示对应的菜单,点击菜单,才能查看相关的界面

    • 界面的控制

      如果用户没有登录,手动在地址栏敲入管理界面的地址,则需要跳转到登录界面

      如果用户已经登录,可是手动敲入非权限内的地址,则需要跳转404界面

    • 按钮的控制

      在某个菜单的界面中,还得根据权限数据,展示出可进行操作的按钮,比如删除,修改,增加

    • 请求和响应的控制

      如果用户通过非常规操作,比如通过浏览器调试工具将某些禁用的按钮变成启腆状态,此时发的请求,也应该被前端所拦截

后台管理系统的权限管理

菜单控制

我现在登录就让后台把路由信息发给我,但是我要用到菜单 页面,这时候我就要使用vuex来共享路由信息;在state中写一个空数组rightList[]接收login页面提交过来的权限数据。映射到菜单页面

  • 当我登陆过后刷新页面时菜单栏消失,为什么?怎么解决

    因为当我登陆过后重新加载时,我的vuex也被刷新,但是vuex的初始值我放的是一个空数组,而且此时我们也没有进行那个登陆操作了。所以就导致我的rightList从有数据变成了没数据⇒我渲染的菜单栏消失。

    state:{
    	rightList:JSON.parse(sessionStorage.getItem('rightList')||'[]')
    },
    mutations:{
    	setRightList(state,data){
    		state.rightList = data
    		sessionStorage.setItem('rightList',JSON.stringify(data))
    	}
    }
    
  • 当我点击退出之后,我需要清空sessionStorage和vuex中的数据
    在点击登出的这个事件中除了写跳转到登录页面还要写清除操作

    logout(){
    //删除sessionStorage数据
    sessionStorage.clear()
    this.$router.push('/login')
    //删除vuex中的数据,让当前的界面刷新(先跳转再刷新)
    window.location.reload()
    }
    

界面控制

正常的逻辑是通过登录界面,登录成功之后跳转到管理平台界面,但是如果用户直接敲入管理平台的地址,也是可以跳过登录的步骤.所以应该在某个时机判断用户是否登录

  • 如何判断用户是否登陆

    在登录时把后台接口中返回的路由数据中的token存起来,
    有token就是登录,没就是没登录
    sessionStorage.setItem('token',res.data.token)
    
    
  • 什么时机判断

    使用路由导航守卫

    router.beforeEach((to,from,next)=>{
    		if(to.path==='/login'){
    			next()
    	}else{
    		const token = sessionStorage.getItem('token')
    		if(!token){
    			next('/login')
    		}else{
    				next()
    		}
    	}
    })
    
  • 还有一个问题,当我登陆之后,我在地址栏中敲下一个不属于我的权限界面,我依旧可以进入(例如我是一个普通用户,我不能访问角色管理界面,但是我在地址栏中敲下角色管理的界面地址,我依旧能访问)这如何阻止?

  • 可以依旧在路由导航中继续判断,但是路由导航守卫固然可以在每次路由地址发生变化的时候,从vuex中取出rightList判断用户将要访问的界面,这个用户到底有没有权限.不过从另外一个角度来说,这个用户不具备权限的路由,是否也应该压根就不存在呢?

    import store from '@/store'
    
     const userRoles= {path:'/users',component:Users}
     const rolesRoles= {path:'/roles',component:Roles}
     const goodsRoles=  {path:'/goods',component:Goods}
    const cateRoles= {path:'/categories',component:Categories}const ruleMapping= {
    	'users':userRoles,
    	'roles':rolesRoles,
    	'goods':goodsRoles,
    	'categories':cateRoles
    }
    {
    path:'/home',
    component:Home,
    redirect:'welcome',
    children:[
    		{path:'/welcome',component:Welcome}
        //{path:'/users',component:Users}
        //{path:'/roles',component:Roles}
     	  //{path:'/goods',component:Goods}
    		//{path:'/categories',component:Categories}
    	]
    }
    
    export function initDynamicRoutes(){
    //根据二级权限,对路由规则进行动态的添加
    		const currentRoutes = router.options.routes
    //遍历二级权限 通过vuex中的store拿到我们的rightList数据
    const rightList = store.state.rightList
    rightList.forEach(item=>{
    		item.children.forEach(item=>{
    			//item二级权限,动态添加到children
    				const temp = ruleMapping[item.path]
    					temp.meta = item.rights
    				currentRoutes[2].children.push(temp)
    		})
    })
    		router.addRoutes(currentRoutes)
    }
    
    export default router
    

登录成功之后动态添加

根据用户所具备的权限,去动态的添加路由规则(还是那个登陆里后台返回的数据)
import{initDynamicRoutes }from '@/router.js'

initDynamicRoutes()//在这个页面刷新界面之后,
//router.js这个页面当然也会重新加载,
//此时所有路由初始化,动态路由不复存在,
//重新登录成功之后才能使动态路由重新操作
//刷新之后我也要动态添加路由规则
这个步骤放到app.vue中

在这个页面我刷新界面之后,router.js这个页面当然也会重新加载,此时所有路由初始化,动态路由不复存在,页面空白。重新登录成功之后才能使动态路由重新操作,刷新之后我也要动态添加路由规则

我刷新之后我也要动态添加路由规则,我将这个放到app.vue中的created()生命周期,这样刷新依旧保存了路由规则

App.vue
import{initDynamicRoutes }from '@/router.js'

created(){
initDynamicRoutes ()
}

所以initDynamicRoutes 有两个时机被调用,第一个时机是在登录成功之后被调用,登陆成功之后我在界面上刷新的话,这时候走的时候根组件当中的created的动态添加


按钮控制

虽然用户可以看到某些界面了,但是这个界面的一些按钮,该用户可能是没有权限的.因此,我们需要对组件中的一些按钮进行控制.用户不具备权限的按钮就隐藏或者禁用,而在这块中,可以把该逻辑放到自定义指令中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cmjVqZSO-1662525080962)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/a17bfdfa-6bd5-4b91-95da-c53474aa309d/Untitled.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-63kHPIKH-1662525080963)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/341bd4fb-5bae-4dff-9d68-f29faee4b61e/Untitled.png)]

  • 在按钮之上添加一个自指令

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YPMQknaP-1662525080963)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6ca4b5f7-e25c-43df-9272-c63a05b33e74/Untitled.png)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ErgwxoV7-1662525080964)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/54051e4e-2d6e-464c-85a3-6b2b4518b4ca/Untitled.png)]

    <el-button type="primary" 
    @click="addDialogVisible=true"
     v-permission="{action:'add',effect:'disabled'}">
    添加
    </el-button>
    
    实现自定义指令=>在utils中写一个permission.js文件实现自定义指令的一个注册
    import Vue from 'vue'
    import router from '@/router.js'
    Vue.directive('permission',{
    	inserted(el,binding){
    			const action = binding.value.action//添加
    			const effect = binding.value.effect//禁用
    			判断当前的路由所对应的组件中,如何判断用户是否具备action的权限呢
          权限的数据在rightList中;**可以做一件事情=>相关权限获取的判断**
    		clg(router.currentRoute.meta)//看当前组件的路由规则是什么
    			这时候使用meta来增加这个组件这个用户所具备的权限数据,
    			那么对于permission而言就方便很多很多,就可以解决了
    			if(router.currentRoute.meta.indexOf(action)===-1){
    					if(effect==='disabled'){//禁用
    							el.disabled=true
    							el.classList.add('is-disabled')//ui组件需要用的样式
    					}
    					el.parentNode.removeChild(el) //删除元素button
    			}
    
    		}
    })
    
    该文件在main.js中进行一个注册:import './utils/permission.js'
    

请求和响应的控制

请求控制

除登录之外的每一次请求都需要token,这样服务器才能鉴别你的身份。所以在请求拦截器中写

axios.interceptors.request.use(function(req){
	const currentUrl = req.url
if(currentUrl!=='login'){
		req.headers.Authorization = sessionStorage.getItem('token')
	}
return req
})

如果发出了非权限内的请求,应该直接在前端访问内被阻止,虽然这个请求发到服务器也会被拒绝

import axios from 'axios'
import Vue from 'vue'
import router from '@router.js'
axios.interceptors.request.use(function(req){
	const currentUrl = req.url
if(currentUrl !=='login'){
		req.headers.Authorization = sessionStorage.getItem('token')
	const action = actionMapping[req.method]
const currentRight = 	router.currentRoute.meta
if(currentRight&&currentRight.indexOf(action)===-1){
		//没有权限
		alert('没有权限')
return Promise.reject(new Error('没有权限'))👇🏿
}
	//判断非权限范围内的请求
//router.currentRoute.meta
//判断当前请求的行为
//restful风格请求 👇🏿
//get请求 view
//post请求 add
//put请求  edit
//delete请求 delete
//[add view edit delete]=>meta中这几种情况 
通过请求方式来判断这个请求到底是做什么样的事情,
再看一下这个事情在当前路由规则的权限列表内存不存在,不存在就直接阻止掉
需要映射关系=>请求方式和操作行为的映射关系
const actionMapping = {
	'get':'view',
	'post':'add',
	'put':'edit',
	'delete':'delete'
}
		}
return req
})

响应控制

得到了服务器返回的状态码401,代表token超时或者被篡改了,此时应该强制跳转到登录界面

axios.interceptors.response.use((res)=>{
	if(res.data.meta.status===401){
			router.push('/login')
sessionStorage.clear()
window.location.reload()
	}
return res
})
Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐