首页 > 其他分享 >Vue3路由权限控制

Vue3路由权限控制

时间:2024-09-24 18:24:21浏览次数:3  
标签:const title Vue3 return router import 权限 路由

Vue3路由权限控制

  1. 设置路由:静态路由与动态路由
  • 静态路由:这些是在应用启动时就已经定义好的路由,通常包括一些不需要权限验证的公共页面,如登录页、404页面等。
  • 动态路由:这些路由是根据用户的权限决定的,通常包括需要权限验证的页面。
  1. 登录获取 token / sessionId 等数据,利用 pinia 进行存储
  • 在登录成功后,将 token 或 sessionId 存储到 Pinia 中,并在需要时(如请求后端API获取权限数据时)从 Pinia 中获取。
  1. 将从后端获取的权限数据和路由页面进行映射
  • 登录成功后,从后端获取的权限数据应该与前端定义的路由进行映射(利用 pinia 进行存储),以决定哪些路由对当前用户是可见的。
  1. 路由权限控制
  • 方法一:全局前置守卫
    • 在全局前置守卫中,检查用户的权限,并根据权限数据决定是否允许用户访问某个路由。
    • 如果用户没有权限访问某个路由,重定向用户到另一个页面(如403页面或登录页面)。
  • 方法二:动态添加路由
    • 这种方法适用于需要根据用户权限动态添加路由的场景。
    • 在登录成功后,根据从后端获取的权限数据,动态地向 Vue Router 添加路由。
    • 全局前置守卫在这里主要用于检查用户是否已经登录以及是否有足够的权限访问当前路由。

vue3路由权限控制demo系统 - 商户管理系统 - 涉及的技术栈:Vite + Vue3 + Axios + Vue Router 4 + Pinia

路由权限控制流程 路由守卫 登录 请求成功 获取token 获取权限 路由不存在 /404 路由存在 token 有权限 路由跳转 无权限 /403 !token store重置 重新登录 请求失败 路由配置 静态路由 路由 动态路由 首页 商户管理 商户列表 商户详情 新增商户 发票管理 申请开票 开票记录 员工管理 员工列表 其他... / /login /404 /403 ...

角色分别为admin / merchant / staff时,菜单栏如下:
菜单栏

1. 路由配置

Vue Router 4进行路由管理时,使用createRouter()函数来创建并配置路由器实例。
createRouter()函数替代了Vue Router 3中的new VueRouter()函数。

// v3.x 示例
// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router);

// 1. 定义 (路由) 组件。
import Layout from '../views/layout/Layout'

const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }

// 2. 定义路由
const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar },
  // ...
]

// 3. 创建 router 实例并导出
const router = new VueRouter({
  // history: 地址栏无'#'号,需要配置服务器重写规则,否则刷新页面会404; 
  // hsah: 地址栏有'#'号,改变 hash 不会重新加载页面。
  mode: 'history', 
  routes //  (缩写) 相当于 routes: routes
})

export default router

// 4. 在main.js中挂载
import router from './router'
const app = new Vue({
  router
}).$mount('#app')
// v4.x 示例
// 1. 引入createRouter和createWebHistory 
import { createMemoryHistory, createRouter } from 'vue-router'

// 2. 引入路由组件
import HomeView from './HomeView.vue'
import AboutView from './AboutView.vue'

// 3. 定义路由
const routes = [
  { path: '/', component: HomeView },
  { path: '/about', component: AboutView },
]

// 4. 创建 router 实例并 export
const router = createRouter({
  // 历史模式:
  // Hash 模式: createWebHashHistory()
  // HTML5 模式: createWebHistory()
  // Memory 模式: createMemoryHistory() - 不能前进或后退,适合 Node 环境和 SSR
  history: createMemoryHistory(),
  routes,
})
export default router
// 5. 在main.js中挂载
import router from './router'
const app = createApp(App)
app.use(router)
app.mount('#app')

// 6. 在App.vue中使用RouterView来渲染路由组件。
// RouterView: 可以使 Vue Router 知道你想要在哪里渲染当前 URL 路径对应的路由组件。
<template>
  <RouterView></RouterView>
</template>
<script setup>
import { RouterView } from 'vue-router'
</script>
  

➡️ @/router/index.js

import { createRouter, createWebHistory } from 'vue-router'
import Login from '../views/login/index.vue'
import Layout from '../views/layout/index.vue'
import { dynamicRoutes } from './dynamic-router'

// 静态路由
export const constantRoutes = [
  {
    path: '/',
    component: Layout,
    redirect: '/home',
    children: [
      {
        path: 'home',
        name: 'home',
        component: () => import('../views/home/index.vue'),
        meta: { title: '首页' }
      }
      // 在此添加更多的动态子路由  
    ]
  },
  {
    path: '/login',
    name: 'login',
    component: Login
  },
  {
    path: '/404',
    name: '404',
    component: () => import('../views/error-page/404.vue')
  },
  {
    path: '/403',
    name: '403',
    component: () => import('../views/error-page/403.vue')
  }
]

// 添加动态路由
constantRoutes.find(item => item.path === '/').children.push(...dynamicRoutes)

const router = createRouter({
  history: createWebHistory(),
  routes: constantRoutes
})

export default router

➡️ @/router/dynamic-router.js

import merchantList from '../views/merchant-management/merchant-list.vue'

// 动态路由,需要配置权限
export const dynamicRoutes = [
  {
    name: 'merchant',
    path: 'merchant',
    meta: { title: '商户管理'},
    redirect: '/merchant/list',
    children: [
      {
        path: 'list',
        name: 'merchant-list',
        component: merchantList,
        meta: { title: '商户列表'}
      },
      {
        path: 'info',
        name: 'merchant-info',
        component: () => import('../views/merchant-management/merchant-info.vue'),
        meta: { title: '商户详情'}
      },
      {
        path: 'add',
        name: 'merchant-add',
        component: () => import('../views/merchant-management/merchant-add.vue'),
        meta: { title: '新增商户'}
      }
    ]
  },
  {
    name: 'invoice',
    path: 'invoice',
    meta: { title: '发票管理'},
    redirect: '/invoice/list',
    children: [
      {
        path: 'list',
        name: 'invoice-list',
        component: () => import('../views/invoice-menagement/invoice-list.vue'),
        meta: { title: '开票记录'}
      },
      {
        path: 'apply',
        name: 'invoice-apply',
        component: () => import('../views/invoice-menagement/invoice-apply.vue'),
        meta: { title: '申请开票'}
      }
    ]
  },
  {
    name: 'staff',
    path: 'staff',
    meta: { title: '员工管理' },
    redirect: '/staff/list',
    children: [
      {
        path: 'list',
        name: 'staff-list',
        component: () => import('../views/staff-management/list.vue'),
        meta: { title: '员工列表'}
      }
    ]
  }
]

2. 基于 Axios 的 http 请求封装

vite.config.js 中配置代理服务:

当浏览器发出一个请求时,如果请求URL的协议、域名或端口与当前页面URL中的任何一个不同,则该请求被认为是跨域请求。浏览器出于安全考虑,默认会阻止这种跨域请求。在开发环境中,如使用Vite时,可以通过配置代理服务器来解决跨域问题,即让Vite作为中间代理,接收来自客户端的请求,并将其转发至目标服务器;服务器响应后,Vite再将结果返回给客户端。
注意:Vite反向代理仅在开发环境有用,如果打包了,就不会走反向代理了。这是因为打包后的Vue3项目会被部署到与后端相同的IP和端口下,此时不会有跨域问题。

➡️vite.config.js - 配置反向代理

import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [
    vue(),
  ],
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  server: {
    host: '0.0.0.0', // 主机ip
    https: false, // 是否开启 https
    // open: true, // 是否自动在浏览器打开
    port: 5173, // 端口号默认值5173
    proxy: {
      '/api': {
        target: 'http://localhost:3000', // 后台接口前缀
        changeOrigin: true, // 是否允许跨域
        secure: false, // 是否忽略 HTTPS 证书错误
        rewrite: (path) => path.replace(/^\/api/, ''),
      }
    }
  },
  
})

创建 .env.development.env.production 文件:

  • 开发环境:
    • 使用命令 npm run dev 编译 .env.development 文件。
    • 我们可以在 .env.development 文件里定义 VITE_BASE_URL = "/api"
    • 然后在接口文件(例如 @/utils/http.js)中取出 VITE_BASE_URL 并设置为 baseURL,这样可以统一给接口地址加上 /api 前缀。这样就能和 vite.config.js 中的反向代理关键字 /api 联系起来,所有以 /api 开头的接口都会被代理到所配置的 URL,从而解决跨域问题。
  • 生产环境:
    • 使用命令 npm run build 编译 .env.production 文件。
    • 在生产环境中,我们将 VITE_BASE_URL 设置为接口的 IP 地址。这样接口文件的 baseURL 就会得到实际的 IP 地址,生产环境通常不会出现跨域问题,因此不需要走代理。

➡️.env.development

VITE_BASE_URL = '/api'

➡️.env.production

VITE_BASE_URL = 'https://xxxx.com/'

➡️ @/utils/baseUrl.js

export const baseUrl = import.meta.env.VITE_BASE_URL

➡️ @/utils/http.js

import axios from 'axios'  
import baseURL from './baseURL'  
import { ElMessage } from 'element-plus'  

// 创建一个 axios 实例
const instance = axios.create({  
    timeout: 5000,  
    baseURL: baseURL,  
})  
  
// 请求拦截器  
instance.interceptors.request.use(  
    config => {  
        // 请求头添加token
        // if (store.state.UserToken) {  
        //     config.headers.Authorization = `Bearer ${store.state.UserToken}`  
        // }  
        return config  
    },  
    error => Promise.reject(error)  
)  
  
// 响应拦截器  
instance.interceptors.response.use(  
    response => {  
        // 这里可以根据需要处理响应数据
        return response.data  
    },  
    error => {  
        console.error('API 请求错误:', error)  
        if (error.response) {  
            const status = error.response.status  
            let message = '请求错误'  
            switch (status) {  
                case 400:  
                    message = '请求出错'  
                    break  
                case 401:  
                    ElMessage.warning('授权失败,请重新登录')  
                    // 此处可以更新登录状态 
                    // store.commit('LOGIN_OUT')  
                    // setTimeout(() => {  
                    //     window.location.reload()  
                    // }, 1000)  
                    return  
                case 403:  
                    message = '拒绝访问'  
                    break  
                case 404:  
                    message = '请求错误,未找到该资源'  
                    break  
                case 500:  
                    message = '服务器端出错'  
                    break  
                default:  
                    message = `请求错误 [${status}]`  
            }  
            ElMessage.error(message)  
        } else {  
            // 处理网络错误 
            ElMessage.error('连接服务器失败')  
        }  
        return Promise.reject(error)  
    }  
)  
  
// 封装 GET 和 POST 方法  
const http = {  
    get: (url, options = {}) => instance.get(url, options),  
    post: (url, data, options = {}) => instance.post(url, data, options),  
    // 可以继续添加其他 HTTP 方法,如 put, delete 等  
}  
  
export default http

➡️ @/api/index.js - 封装api

import http from "@/utils/http"

export function loginAPI(data) {
  return http.get(`/user/login?username=${data.username}&password=${data.password}`)
}

export function getPermissionRoutesAPI(data) {
  return http.post('/user/getpermissionRoutes', data)
}

3. mock.js模拟后端接口数据

➡️ @/mock/index.js

import Mock from 'mockjs'
import user from './modules/user'
import permission from './modules/permission'

const { mock } = Mock // Mock函数

// 拦截命中的请求
mock(RegExp("/api/user/login" + ".*"), 'get', user.loginAPI)
mock("/api/user/getpermissionRoutes", 'post',permission.getPermissionRoutesAPI)

➡️ @/mock/modules/user.js

import { Random } from "mockjs" // 导出随机函数

function loginAPI(req) {
  // req是一个请求对象,包含: url,type和body等属性
  const url = req.url.split('?')[1]
  const username = url.split('&')[0].split('=')[1]
  const password = url.split('&')[1].split('=')[1]
  let token = ''
  let message = '登录成功'
  let code = 200
  if (username === 'admin') {
    token = 'admin-' + Random.guid()
  } else if (username === 'merchant') {
    token = 'merchant-' + Random.guid()
  } else if (username === 'staff'){
    token = 'staff-' + Random.guid()
  } else {
    code = 401
    token = ''
    message = '账号不存在'
  }
  const data = {
    code: code, // 响应状态码
    data: {
      username: Random.cname(),
      token: token 
    },
    message: message
  }
  return data
}

export default {
  loginAPI,
}

➡️ @/mock/modules/permission.js

const adminPermission = {
  "code": 200,
  "message": "获取权限成功",
  "data": [
    {
      "title": "首页",
    },
    {
      "title": "商户管理",
      "children": [
        {
          "title": "商户列表"
        },
        {
          "title": "商户详情",
        },
        {
          "title": "新增商户"
        }
      ]
    },
    {
      "title": "员工管理",
      "children": [
        {
          "title": "员工列表"
        }
      ]
    },
    {
      "title": "发票管理",
      "children": [
        {
          "title": "申请开票"
        },
        {
          "title": "开票记录"
        }
      ]
    }
  ]
}
const merchantPermission = {
  "code": 200,
  "message": "获取权限成功",
  "data": [
    {
      "title": "首页",
    },
    {
      "title": "员工管理",
      "children": [
        {
          "title": "员工列表"
        }
      ]
    },
    {
      "title": "发票管理",
      "children": [
        {
          "title": "申请开票"
        },
        {
          "title": "开票记录"
        }
      ]
    }
  ]
}
const staffPermission = {
  "code": 200,
  "message": "获取权限成功",
  "data": [
    {
      "title": "首页",
    },
    {
      "title": "发票管理",
      "children": [
        {
          "title": "申请开票"
        },
        {
          "title": "开票记录"
        }
      ]
    }
  ]
}

function getPermissionRoutesAPI(req, res) {
  const token = req.body
  switch (token.split('-')[0]) {
    case 'admin':
      return adminPermission
    case 'merchant':
      return merchantPermission
    case 'staff':
      return staffPermission
    default:
      return staffPermission
  }
}

export default {
  getPermissionRoutesAPI
}

标签:const,title,Vue3,return,router,import,权限,路由
From: https://blog.csdn.net/m0_46714106/article/details/142379910

相关文章

  • OSPF 默认路由的发布原则 | 类型详解
    默认路由默认路由是指目的地址和掩码都是0的路由。当设备无精确匹配的路由时,就可以通过默认路由进行报文转发。一般多用于网络边界路由器访问互联网所需要的一条路由。同时,企业内,在精确的内部路由基础上,边界路由器通告一条默认路由,使所有访问未知目的地的数据报文都送至......
  • 如何在 CentOS 中进入 root 权限
    作为一名服务器管理员,有时您需要以root用户身份执行任务以进行管理操作。以下是两种在CentOS中执行此操作的方法:方法1:使用sudosudo命令允许您以root用户的身份执行特定命令,而无需更改用户会话。要使用此方法:在终端中键入以下命令:1sudo-i系统会提......
  • 软路由系统 --- OpenWrt下载安装中文语言包
    刚安装好的OpenWrt登录Web管理后台后,发现界面是英文的,在系统的语言选项也只有English,没有中文可切换,那该如何呢?那我们就给它安装个中文的语言包,再来进行切换,看看能行不能行!如下介绍三种方法进行安装中文语言包。openwrt系统:OpenWrt版本:22.03.5中文语言包:luci-i18n-base-zh-cn方法......
  • Vue3 流程图组件库 :Vue Flow
    VueFlow是一个轻量级的Vue3组件库,它允许开发者以简洁直观的方式创建动态流程图。本篇文章记录一下VueFlow的基本用法安装npmadd@vue-flow/core流程图的构成Nodes、Edges、Handles主题默认样式通过导入样式文件应用/*thesearenecessarystylesforvueflow*/@import'......
  • MySQL深度探索:掌握触发器自动化与精细用户权限管理,提升数据库效能与安全
     作者简介:我是团团儿,是一名专注于云计算领域的专业创作者,感谢大家的关注 座右铭:   云端筑梦,数据为翼,探索无限可能,引领云计算新纪元 个人主页:团儿.-CSDN博客目录前言:触发器(Triggers):用户权限(UserPermissions):一.触发器1.MySQL触发器简介2.引发触发器执行的事件,......
  • 利用vscode-icons-js在Vue3项目中实现文件图标展示
    背景:在开发文件管理系统或类似的项目时,我们常常需要根据文件类型展示对应的文件图标,这样可以提高用户体验。本文将介绍如何在Vue3项目中利用vscode-icons-js库,实现类似VSCode的文件图标展示效果。先看效果:一、引入vscode-icons-js首先,我们需要安装vscode-icons-js库。你可以使用n......
  • Vue3 注册及使用全局方法
    一、背景自己的一个考公网站,需求是用户登录系统,但是用户去查看功能时要判断当前用户是否有是会员,如果不是会员,那么查看其他功能时需要弹窗说不是vip,如果是会员则可以直接查看。二、实现首先上截图。 1.Vue3全局注册方法1.新建popToast.jsimportCookiesfrom"js-c......
  • 万象更新 Html5 - vue.js: vue 路由基础
    源码https://github.com/webabcd/Html5作者webabcd万象更新Html5-vue.js:vue路由基础示例如下:vue\router\sample1.html<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>vue路由基础</titl......
  • Oracle数据库中创建用户并对用户授予特定的视图访问权限。
    1.创建用户名和密码---创建用户名密码createuseruseridentifiedbypassword;--userpassword分别为用户名及密码登录用户使用defaulttablespacetsp_ehis_indextemporarytablespacetsp_ehis_tempprofiledefault;2.对用户进行相对应的授权增加用户的......
  • React的useId,现在Vue3.5终于也有了!
    前言React在很早之前的版本中加了useId,用于生成唯一ID。在Vue3.5版本中,终于也有了期待已久的useId。这篇文章来带你搞清楚useId有哪些应用场景,以及他是如何实现的。关注公众号:【前端欧阳】,给自己一个进阶vue的机会useId的作用他的作用也是生成唯一ID,同一个Vue应用里面每次调用......