动态router权限

权限

1、后端返回角色(前端去配置)

2、后端返回数据

第一种方法后端返回数据(前端配置)

一、mock模拟的对比数据

推荐 https://www.fastmock.site/ 网站 模拟接口的

{
  "code": "200",
  "message": "登录成功",
  "meunList": [{
      "name": "home"
    },
    {
      "name":"detail"
    },
    {
      "name": "form",
      "children": [{
        "name": "form1"
      }]
    },
    {
      "name": "transition",
      "children": [{
          "name": "transition1"
        },
        {
          "name": "transition2"
        }
      ]
    },
    {
      "name": "test",
      "children": [{
          "name": "test1"
        },
        {
          "name": "test2"
        }
      ]
    },
  ]
}
二、 layout

在这里插入图片描述

  • silder.vue
利用的vuex里面的数据
<template>
  <!-- 侧边栏 -->
  <div class="sliber-container" style="width: 256px">
    <a-menu
      :selectedKeys="[$route.path]"   // 让其手动刷新的时候还能定位到原来的url
      mode="inline"
      theme="dark"
      :inline-collapsed="$store.state.collapsed"
      @select="handleSelect"
    >
      <template v-for="item in $store.state.oneRouter.silderbarMeun">
        <a-menu-item v-if="!item.children" :key="'/'+item.name">
          <a-icon type="mail" />
          <span>
            {{item.meta.title}}
          </span>
        </a-menu-item>
        <a-sub-menu v-else :key="item.name">
            <span slot="title">
              <a-icon type="mail" />
              <span>{{item.meta.title}}</span>
            </span>
            <a-menu-item v-for="$1 in item.children" :key="'/'+item.name + '/' + $1.name">
              {{$1.meta.title}}
            </a-menu-item>
         </a-sub-menu>
      </template>     
    </a-menu>
  </div>
</template>
  • header.vue
<template>
  <!-- 进入首页后我们要使用localStorage
        把登陆时存储的用户名取出来显示在右边登录名的地方,
        当用户点击注销图标的时候,清除localStorage,
        并跳转回登陆页面。 -->
  <!-- 头部 -->
  <div class="header_container">
    <div class="header_left">
      <span>恓惶后台系统</span>
    </div>
    <div class="header_right">
      <a-avatar icon="user" />
      <span class="user-name" @click="exitFn">登出</span>
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {};
  },
  methods: {
    exitFn() {
      // 退出的时候得撤销token
      localStorage.removeItem("token");
      this.$store.commit('LOGIN_OUT')

      // this.$store.commit('oneRouter/CLEAR_PERMISSION', {}, {root: true})
      // this.$store.commit('oneRouter/CLEAR_MEUN', {}, {root: true})
      window.location.reload()  // 有这个就相当于刷新就不需要上面两个清楚步骤了
      this.$router.push("/login");
    },
  },
};
</script>

三、vuex
  • 目录
    在这里插入图片描述
  • index.js
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
import way from './modules/index'

export default new Vuex.Store({
  state: {
    // 这样写的目的是获取的时候直接获取,设置的时候直接设置()
    /**
     * 为什么要这样设置呢,因为vuex是存在内存中的这样读取的更快
     */     
    get UserToken(){
      return localStorage.getItem('token')
    },
    set UserToken(value){
      localStorage.setItem('token', value)
    }  
  },
  mutations: {
    LOGIN_IN(state, token){
      state.UserToken = token
    },
    LOGIN_OUT(state){
      state.UserToken = ""
    }
  },
  actions: {},
  modules: {
    oneRouter: way.oneRouter
  },
});

  • 模块中的index.js
/**
 * @params oneRouter 路由权限的方法一(前端比对)
 */
import oneRouter from './oneRouter.js'
export default {
  oneRouter
}
  • oneRouters.js

import { getVipData, getMainData } from '@/api/nav.js' // 接口一个数据就类似上面那种配置
import dynamicRouter from '@/router/dynamic_router'	// 拿来对比做权限的全部路由(数据在下方)
import { recursionRouter, setDefaultRouter} from '../../utils/recursion' // 两个转换方法(数据在下方)
import router, { DynamicRoutes} from '@/router/index'
import store from '@/store'
export default {
  namespaced: true,
  state:{
    permissionList: null, // 最終所有的路由用来在导航守卫判断用的
    silderbarMeun:[], // 导航菜单 用来渲染的

  },
  getters:{},
  mutations:{
    SET_PERMISSION(state,routes){
      state.permissionList = routes
    },
    CLEAR_PERMISSION(state){
      state.permissionList = null
    },
    SET_MEUN(state,meun){
      state.silderbarMeun = meun
    },  // 设置菜单的
    CLEAR_MEUN(state,meun){
      state.silderbarMeun = []
    }  // 清楚菜单的
  },
  // 异步访问
  actions: {
    async FETCH_PERMISSION({commit, state}){
      let permissionList = null
      if(store.state.UserToken == 'vip'){
        permissionList = await getVipData()
      }else{
        permissionList = await getMainData()
      }
      // 筛选
      const routes = recursionRouter(permissionList.data.meunList, dynamicRouter)
      const Main = DynamicRoutes.find(el => el.name === 'layout')
      Main.children = [] // 防止退出的时候这里面的东西还存在(如果退出直接用window.location.reload()这个步骤可有可无)
      const children = Main.children
      children.push(...routes)
      // 生成菜单
      commit('SET_MEUN', children)
      // 设置默认路由
      setDefaultRouter([Main]) // 可有可无我是在全部定义中已经定义好重定向了
      // 初始化路由
      const initRoute = router.options.routes
      router.addRoutes(DynamicRoutes)
      commit('SET_PERMISSION',[...initRoute, ...DynamicRoutes])
    }
  }
}

四、router/index.js
import Vue from "vue";
import VueRouter from "vue-router";
import store from "@/store";
Vue.use(VueRouter);
// 引入自己写的layout
import Layout from "@/layout";

// 防止多点 重写router的push方法
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}

// 初始化路由
const routes = [
  {
    path: "/login",
    name: "login",
    component: () => import("@/views/login.vue"),
    meta: { requiresAuth: true, title: "登录" },
  }
];

/**
 * 动态路由
 */
// 动态加载路由
export const DynamicRoutes = [
  {
    path: "/",
    name:'layout',
    component: Layout,
    meta: { requiresAuth: true, title: "首页" },
    children: [
    ],
  },
  //匹配404页面  匹配上面所有的就到这个页面
  {
    path: "*",
    name: "404",
    component: () => import("@/views/404"),
  },
]

const router = new VueRouter({
  routes,
});

// 设置路由白名单(其实网上很多做法用meta的requiresAuth就是跟白名单一样的)
function inWhiteList(toPath) {
  const whiteList = ['/login','/404'];
  const bool = whiteList.some(el => el == toPath) // 有一个正确就正确
  return bool
}
// 导航守卫
router.beforeEach(async (to, from, next) => {
  window.document.title = to.meta.title // 改变浏览器标题
  // 路由守卫(可以做权限),根据路由元信息设置文档标题
  const token = store.state.UserToken;
  // 1.检查to.path是否存在于免登陆白名单
  if (inWhiteList(to.path)){
    // 判断存在token的时候并且是login的时候直接跳转
    if (to.path === '/login' && token) {
      // 避免重复登录
      next({ path: '/'})
    } else {
      next()
    }
    return
  }
  // 2.登录逻辑
  if (!token) {
    // 用户未登录
    next({
      path: '/login'
    })
  } else {
    // 用户登录了
    if(!store.state.oneRouter.permissionList){
      // router.matcher = new VueRouter({routes}) // 请求路由里面的纪录 要保留之前静态的路径
      store.dispatch("oneRouter/FETCH_PERMISSION").then(() => {
        // 防止刷新
        next({path: to.path})
      })
    }else{
      next()
    }
  }
});


export default router;
  • 全部数据(dynamic_router.js)
/**
 * 全部router数据 用来和返回的数据进行对比
*/
const DynamicRoutes = [
  {
    path:'/home',
    name:'home',
    component: () => import('@/views/home/home.vue'),
    meta: { title: "首页" },
  },
  {
    path:'/detail',
    name:'detail',
    component: () => import('@/views/detail/detail.vue'),
    meta: { title: "详情" },
  },
  {
    path:'/form',
    name:'form',
    component: () => import('@/views/form/index.vue'),
    meta: { title: "表单" },
    children: [
      {
        path:'form1',
        name:'form1',
        component: () => import('@/views/form/form1.vue'),
        meta: { title: "表单1" },
      }
    ]
  },
  {
    path:'/transition',
    name:'transition',
    component: () => import('@/views/transition/index.vue'),
    meta: { title: "过渡" },
    children: [
      {
        path:'transition1',
        name:'transition1',
        component: () => import('@/views/transition/transition1.vue'),
        meta: { title: "过渡1" },
      },
      {
        path:'transition2',
        name:'transition2',
        component: () => import('@/views/transition/transition2.vue'),
        meta: { title: "过渡1" },
      }
    ]
  },
  {
    path:'/test',
    name:'test',
    component: () => import('@/views/test/index.vue'),
    meta: { title: "测试" },
    children: [
      {
        path:'test1',
        name:'test1',
        component: () => import('@/views/test/test.vue'),
        meta: { title: "测试1" },
      },
      {
        path:'test2',
        name:'test2',
        component: () => import('@/views/test/test1.vue'),
        meta: { title: "测试2" },
      }
    ]
  },
]

export default DynamicRoutes
  • recursionRouter, setDefaultRouter两种方法
/**
 * 这个是对比发的文件
 * 方法一:比对路由权限
 * 方法二:指定返回的重定向默认
 */
/**
 * @param {Array} userRouter 后端返回的数据
 * @param {Array} allRouter 前端自己配置好的路由
 */
export function recursionRouter(userRouter = [], allRouter = []){
  let resultRouter = []
  allRouter.forEach((v,i) => {
    userRouter.forEach((item,index) => {
      if(item.name === v.name){
        if(item.children && item.children.length > 0){
          v.children = recursionRouter(item.children, v.children)
        }
        resultRouter.push(v)
      }
    })
  })
  return resultRouter
}

export function setDefaultRouter(routes){
  routes.forEach((v,i) => {
    if(v.children && v.children.length > 0){
      v.redirect = {name: v.children[0].name}
      setDefaultRouter(v.children)
    }
  })
}
五、封装request.js
import axios from 'axios'

const http = axios.create({
    baseURL: 'https://www.fastmock.site/mock/772b8327e1a2bc2488c1e8329f0e917b/main',
    timeout: 10000,    
});

// 添加请求拦截器
http.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
});

// 添加响应拦截器
http.interceptors.response.use(function (response) {
    // 对响应数据做点什么
    return response;
  }, function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
});

export default http

注意最好在addroute后(效果有用),在把router.option.routes中也得添加下动态数据router,避免在组件中获取不到里面的数据

六、Bug
  • 使用导航菜单重复点击报错解决方法

    在router下的index.js下 在new VueRouter(之前定义)
    const resolveOriginal = VueRouter.prototype.push
    VueRouter.prototype.push = function push(location) {
      return resolveOriginal.call(this, location).catch(err => err)
    }
    
七、额外的知识点

设置浏览器的标题和图标

  • 标题的改变
    在这里插入图片描述
  • 将.ico格式的图片放到public路径下面 之后自己取名字就好
    在这里插入图片描述
    在这里插入图片描述
八、登录

1.登录的流程

在这里插入图片描述

还有一点注意的就是之后在自己封装axios的时候要在请求拦截器中

并且登录的时候我们还要将token存到vuex里面去,这样取出来的时候方便

2.跨域的问题

  • 解决方法一
    vue.config.js配置
    
    devServer: {
        open: true, //配置后自动启动浏览器
        // proxy: 'http://localhost:8080'   // 配置跨域处理,只是一个代理
        proxy: {
          //配置多个跨域
          "/api": {  // 拦截以/api开头接口
            target: "https://www.bilibili.com/",   //目标
            changeOrigin: true,  // //这里true表示实现跨域
            ws: true, //websocket支持
            secure: false,   
            pathRewrite: {
              "^/api": "",
            },
          },
        },
      },
      组件调用直接  /api/index/ding.json
    
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue Router是Vue.js官方的路由管理插件,它可以帮助我们实现页面的跳转和管理。在实际开发中,我们经常需要根据用户权限来控制页面的访问,这时候就需要用到Vue Router权限管理功能。 Vue Router权限管理分为两种:全局守卫和路由守卫。 1. 全局守卫 全局守卫是在路由跳转之前对所有路由进行拦截的一种机制。在Vue Router中,我们可以通过beforeEach方法来实现全局守卫。beforeEach会在每次路由跳转之前被调用,我们可以在这个方法中判断用户的权限,如果用户没有权限访问该路由,则可以跳转到其他页面或者提示用户无权访问。 具体实现代码如下: ```javascript router.beforeEach((to, from, next) => { // 判断用户是否登录 if (!store.state.isLogin) { // 如果用户没有登录,则跳转到登录页 next('/login'); } else { // 如果用户已登录,则判断用户是否有权限访问该路由 if (to.meta.requiresAuth && !store.state.hasPermission) { // 如果用户无权限访问该路由,则跳转到其他页面或者提示用户无权访问 next('/'); } else { // 如果用户有权限访问该路由,则直接跳转到该路由 next(); } } }); ``` 在上面的代码中,我们首先判断用户是否登录,如果用户没有登录,则跳转到登录页。如果用户已登录,则判断用户是否有权限访问该路由。如果用户无权限访问该路由,则跳转到其他页面或者提示用户无权访问;如果用户有权限访问该路由,则直接跳转到该路由。 2. 路由守卫 路由守卫是对单个路由进行拦截的一种机制。在Vue Router中,我们可以通过设置meta字段来定义路由守卫。在路由跳转之前,我们可以通过beforeEnter方法来判断用户的权限,如果用户没有权限访问该路由,则可以跳转到其他页面或者提示用户无权访问。 具体实现代码如下: ```javascript const routes = [ { path: '/', component: Home, meta: { requiresAuth: true // 需要登录才能访问 } }, { path: '/about', component: About, meta: { requiresAuth: true, // 需要登录才能访问 requiresPermission: true // 需要特定权限才能访问 }, beforeEnter: (to, from, next) => { // 判断用户是否有特定权限 if (!store.state.hasPermission) { // 如果用户没有特定权限,则跳转到其他页面或者提示用户无权访问 next('/'); } else { // 如果用户有特定权限,则直接跳转到该路由 next(); } } }, { path: '/login', component: Login } ]; ``` 在上面的代码中,我们首先在路由的meta字段中设置了requiresAuth和requiresPermission属性。requiresAuth表示该路由需要登录才能访问,requiresPermission表示该路由需要特定权限才能访问。然后我们在路由的beforeEnter方法中判断用户是否有特定权限,如果用户没有特定权限,则跳转到其他页面或者提示用户无权访问;如果用户有特定权限,则直接跳转到该路由。 总结: Vue Router权限管理可以帮助我们根据用户的权限控制页面的访问。全局守卫和路由守卫是实现权限管理的两种机制,它们可以结合使用来实现更加复杂的权限控制逻辑。在实际开发中,我们需要根据具体的业务需求和安全要求来设计和实现权限管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值