16-Vue3 动态路由权限控制
Vite2+Vue3+TypeScript+Element-plus脚手架搭建系列✅01-初始化 Vite 项目✅02-配置 Vite2 环境变量✅03-Vite2 配置及说明✅04-Vue3 使用 SCSS✅05-Vue3 路由配置✅06-TypeScript 配置及说明✅07-Vue3 使用 axios✅08-Vue3 axios 对象封装✅09-ESLint 配置及说明✅10-ESLint
Vite2+Vue3+TypeScript+Element-plus脚手架搭建系列
✅01-初始化 Vite 项目
✅02-配置 Vite2 环境变量
✅03-Vite2 配置及说明
✅04-Vue3 使用 SCSS
✅05-Vue3 路由配置
✅06-TypeScript 配置及说明
✅07-Vue3 使用 axios
✅08-Vue3 axios 对象封装
✅09-ESLint 配置及说明
✅10-ESLint 与 Prettier 集成配置及说明
✅11-Mock.js 模拟接口数据
✅12-Vite2 引入 Element-Plus 框架
✅13-渐变+透明样式实现清爽登录页
✅14-Element-Plus 实现后台管理系统布局
✅15-Pinia 实现 store 状态管理
✅16-Vue3 动态路由权限控制
文章目录
Vue3 动态路由权限控制
🎯 目标
- 实现登录用户与非登录用户权限控制:非登录用户只能访问登录页面 。
- 实现管理员与普通用户的权限控制:管理员能访问
用户管理和角色管理
页面,普通用户只能用户管理
页面。
😴 功课
权限控制相关流程
-
登录成功流程
-
鉴权流程
-
登出流程
路由导航守卫
vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。你可以使用 router.beforeEach 注册一个全局前置守卫:
const router = createRouter({ ... })
router.beforeEach((to, from) => {
// ...
// 返回 false 以取消导航
return false
})
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于等待中。
每个守卫方法接收两个参数:
to
:即将要进入的目标
from
:当前导航正要离开的路由
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next
函数也不会改变导航本身:
router.afterEach((to, from) => {
// 后置处理逻辑
})
其它详情请查看 Vue Router 官方文档
🍸 准备
安装依赖
安装 cookie 操作依赖和进度条依赖。
npm install -S js-cookie @types/js-cookie nprogress @types/nprogress
调整文件&目录
添加 src/utils/cookie/index.ts
文件,存放 token
管理工具方法。
添加 src/utils/progress/index.ts
文件,存放进度条实例。
目录结构如下:
📁 src
----📁 utils
--------📁 token
------------📄 index.ts
--------📁 progress
------------📄 index.ts
🌈 Coding
Token 管理
src/utils/token/index.ts
Token 管理工具方法代码如下:
import Cookies from 'js-cookie'
// ↓cookie 中保存 token 的键
export const tokenKey = 'bee-token'
// ↓获取token
export const getToken = (): string | undefined => {
return Cookies.get(tokenKey)
}
// ↓设置token
export const setToken = (token: string): string | undefined => {
return Cookies.set(tokenKey, token)
}
// ↓删除token
export const removeToken = (): void => {
return Cookies.remove(tokenKey)
}
// ↓判断token是否存在
export const existToken = (): boolean => {
return getToken() !== undefined
}
实例化 NProgress
src\utils\progress\index.ts
实例化 NProgress 对象代码如下:
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
NProgress.configure({
easing: 'ease', // 动画方式
speed: 500, // 递增进度条的速度
showSpinner: false, // 是否显示加载ico
trickleSpeed: 200, // 自动递增间隔
minimum: 0.3, // 初始化时的最小百分比
})
export default NProgress
修改 Login Mock 数据
在 src/mock/login/index.ts
中
- 将登录的 mock 数据改为随机生成的 token。且 admin 用户和普通用户返回 token 长度不一致
- 将获取用户信息的 mock 数据改成 admin 用户和普通用户两种
💡 模拟后端解析 token 返回的权限数据,用 token 长度不一致来区分
......
'adminToken|1': [
{
token: '@STRING( "lower", 24 )',
},
],
'zhangsanToken|1': [
{
token: '@STRING( "lower", 12 )',
},
],
'adminInfo|1': [
{
id: '@INCREMENT()',
username: 'admin',
name: '超级管理员',
avatar: '@IMAGE(100, "#ffc72d", "Code-Bee"),',
menus: [
{
id: 1,
name: '权限管理',
icon: 'el-icon-menu',
children: [
{ id: 2, name: '用户管理', path: '/sys/user', component: '/sys/User.vue' },
{ id: 3, name: '角色管理', path: '/sys/role', component: '/sys/Role.vue' },
{ id: 4, name: '菜单管理', path: '/sys/menu' },
],
},
{
id: 5,
name: '系统管理',
icon: 'el-icon-setting',
children: [
{ id: 6, name: '系统字典', path: '/sys/dict' },
{ id: 7, name: '参数配置', path: '/sys/config' },
{ id: 8, name: '通知公告', path: '/sys/notice' },
{ id: 9, name: '日志审计', path: '/sys/log' },
],
},
],
},
],
'zhangsanInfo|1': [
{
id: '@INCREMENT()',
username: 'zhangsan',
name: '张三',
avatar: '@IMAGE(100, "#ffc72d", "张三"),',
menus: [
{
id: 1,
name: '权限管理',
icon: 'el-icon-menu',
children: [{ id: 2, name: '用户管理', routeName: 'sys-user', path: '/sys/user', component: '/sys/User.vue' }],
},
],
},
],
......
修改登录逻辑
修改 Login.vue
中登录逻辑,登录成功后将 token 保存到 cookie:
......
const signin = () => {
if (!form.username) {
errorMessage('用户名为空')
} else if (!form.password) {
errorMessage('密码为空')
} else {
loginApi.signin(form).then((res: any) => {
// ↓保存token
setToken(res.data.token)
router.push('/')
})
}
}
......
修改登出逻辑
修改 src/components/layout/header/index.vue
中登出逻辑,登出时将 token 删除:
......
const signout = () => {
// ↓将store重置为初始值
useUserInfoStore().$reset()
// ↓删除token
removeToken()
router.push('/login')
}
......
动态路由
src\store\user-info.ts
保存用户信息到 store 时,动态添加路由:
......
actions: {
setAll(userinfo: any) {
const { id, username, name, avatar, menus } = userinfo
this.id = id
this.username = username
this.name = name
this.avatar = avatar
this.menus = menus
menus.forEach((menu: any) => {
if (menu.children) {
menu.children.forEach((sub: any) => {
// ↓动态添加路由
router.addRoute('root', {
path: sub.path,
component: modulesRoutes[`/src/views${sub.component}`],
})
})
}
})
},
},
......
在路由中添加导航守卫
在 src/router/index.ts
将添加全局前置守卫(beforeEach
)与全局后置钩子(afterEach
):
......
// ↓全局前置守卫
router.beforeEach(async (to) => {
NProgress.start()
// ↓如果请求地址不是白名单
if (whiteList.indexOf(to.path) === -1) {
// ↓如果token存在检查store,否则跳转到登录页
if (existToken()) {
// ↓从store获取用户信息
const userInfoStore = useUserInfoStore()
// ↓如果没有用户信息,查询用户信息
if (!userInfoStore.id) {
// ↓查询成功保存用户信息且跳转到目标页
try {
await loginApi.userInfo().then((res: any) => {
console.log(res)
userInfoStore.setAll(res.data)
})
} catch (error) {
// ↓移除token无效
removeToken()
return { name: 'login', query: { redirect: `${to.path}` } }
}
}
} else {
// ↓非白名单且token不存在
return { name: 'login', query: { redirect: `${to.path}` } }
}
}
})
// ↓全局后置钩子
router.afterEach(() => {
NProgress.done()
})
......
将静态路由用户管理
sys-user
注释,因为该路由已经 mock 了,会动态添加
🎭 结果
- 未登录时访问首页会自动跳转到登录页
- 用户登录后可访问首页
- 如果登录 admin 用户,能看到多个菜单,且能访问用户管理和角色管理页面
- 如果登录 zhangsan 用户,能看到用户管理菜单且能访问页面,但在浏览器地址栏输入http://127.0.0.1:3000/#/sys/role 访问角色管理页面,会显示404页面:
本文为博主原创文章,任何个人、团体、机构转载和摘录,请注明出处。
更多推荐
所有评论(0)