vue权限控制和动态路由

news2025/1/11 5:40:37

思路


  • 登录:当用户填写完账号和密码后向服务端验证是否正确,验证通过之后,服务端会返回一个token,拿到token之后(我会将这个token存贮到localStore中,保证刷新页面后能记住用户登录状态),前端会根据token再去拉取一个 user_info 的接口来获取用户的详细信息(如用户权限,用户名等等信息)。
  • 权限验证:通过token获取用户对应的 role,动态根据用户的 role 算出其对应有权限的路由,通过 router.addRoutes 动态挂载这些路由。

路由定义


路由分为两种:constantRoutesasyncRoutes

constantRoutes : 代表那些不需要动态判断权限的路由,如登录页、通用页等。

asyncRoutes : 代表那些需要动态判断权限并通过addRoutes动态添加的页面。

创建router.js


import Vue from "vue";
import VueRouter from "vue-router";
import Layout from "@/layout";

Vue.use(VueRouter);

//通用页面:不需要守卫,可直接访问
export const constRoutes = [
  {
    path: "/login",
    component: () => import("@/views/Login.vue"),
    hidden: true //导航菜单忽略该项
  },
  {
    path: "/",
    component: Layout, //应用布局
    redirect: "/home",
    alwaysShow: true,
    meta: {
      title: "客户管理",  //导航菜单项标题
      icon:"kehu" //导航菜单项图标
    },
    children: [
      {
        path: "/home",
        component: () => import("@/views/Home.vue"),
        name: "home",
        meta: {
          title: "客户列表"
        }
      }
    ]
  }
];

//权限页面:受保护页面,要求用户登录并拥有访问权限的角色才能访问
export const asyncRoutes = [
  {
    path: "/system_manage",
    component: Layout,
    redirect: "/system_set",
    meta: {
      title: "系统设置",
      icon: "set"
    },
    children: [	
      {
        path: "/system_set",
        component: () => import("@/views/system_set.vue"),
        name: "system_set",
        meta: {
          title: "系统设置",
          roles: ["admin", "editor"] // 设置该路由进入的权限,支持多个权限叠加
        }
      },
      {
        path: "/system_organiza",
        component: () => import("@/views/system_origaniza.vue"),
        name: "system_origaniza",
        meta: {
          title: "组织结构",
          roles: ["admin"]
        },
        children:[//三级路由嵌套,还要手动在二级目录的根文件下添加一个 <router-view />
          {path:'/custom_link',name:'custom_link',component:() => import("@/views/custom_link.vue"),meta:{title:'客户联系人'}},
          {path:'/tracking',name:'tracking',component:() => import("@/views/tracking.vue"),meta:{title:'跟踪记录'}}
        ]
      },
      {
        path: "/system_data",
        component: () => import("@/views/system_data.vue"),
        name: "system_data",
        meta: {
          title: "数据字典",
          roles: ["admin"]
        }
      }
    ]
  }
];

const router = new VueRouter({
  mode: "history",
  base: process.env.BASE_URL,
  routes: constRoutes
});

export default router;


复制代码

登录


创建登录页 views/Login.vue

<template>
  <div class="container">
    <h2>用户登录</h2>
    <input type="text" v-model="username" />
    <button @click="login">登录</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      username: ""
    };
  },
  methods: {
    login() {
      /*this.$store
        .dispatch("user/login", { username: this.username })
        .then(() => {
          this.$router.push({
            //   接受路由参数然后跳转
            path: this.$route.query.redirect || "/"
          });
        })
        .catch(error => {
          alert(error);
        });*/
        //调api获取token
    }
  }
};
</script>

复制代码

用户登陆状态维护


vuex根模块实现,./store/index.js

import Vue from "vue";
import Vuex from "vuex";
import user from "./modules/user";
import permission from "./modules/permission";

Vue.use(Vuex);

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: { user, permission },
  getters: {
    roles: state => {
      return state.user.roles;
    }
  }
});
复制代码

user模块-存储token 和 roles ./store/modules/user.js

const state = {
  token: localStorage.getItem("token"),
  roles: []
};

const mutations = {
  SET_TOKEN: (state, token) => {
    state.token = token;
  },
  SET_ROLES: (state, roles) => {
    state.roles = roles;
  }
};

const actions = {
  login({ commit }, userinfo) {
    const { username } = userinfo;
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (username === "admin" || username === "jerry") {
          commit("SET_TOKEN", username);
          localStorage.setItem("token", username);
          resolve();
        } else {
          reject("用户名、密码错误");
        }
      }, 1000);
    });
  },
  getInfo({ commit, state }) {
    return new Promise(resolve => {
      setTimeout(() => {
        const roles = state.token === "admin" ? ["admin"] : ["editor"];
        commit("SET_ROLES", roles);
        resolve(roles);
      }, 1000);
    });
  }
};

export default {
  namespaced: true,
  state,
  mutations,
  actions
};

复制代码

路由守卫


创建./src/permission.js

import router from "./router";
import store from "./store";

const whiteList = ["/login"]; //无需令牌白名单

router.beforeEach(async (to, from, next) => {
  //to and from are Route Object,next() must be called to resolve the hook
  // 获取令牌判断用户是否登录
  const hasToken = localStorage.getItem("token");
  if (hasToken) {
    //已登录
    if (to.path === "/login") {
      //若以登录没有必要显示登录页,重定向回首页
      next({ path: "/" });
    } else {
      // 去其他路由
      const hasRoles =
        store.state.user.roles && store.state.user.roles.length > 0;
      if (hasRoles) {
        // 若用户角色已付加则说明权限以判定,动态路由已添加
        next();
      } else {
        try {
          // 请求获取用户信息
          const roles = await store.dispatch("user/getInfo");
          console.log(roles);
          // 根据当前用户角色动态生成路由
          const accessRoutes = await store.dispatch(
            "permission/generateRoutes",
            roles
          );
          console.log(accessRoutes);
          // 添加这些至路由器
          router.addRoutes(accessRoutes);
          // 继续路由切换,确保addRoutes完成
          next({ ...to });
        } catch (error) {
          // 出错需要重置令牌(令牌过期,网络错误等原因)
          //await store.dispatch('user/resetToken')
          next(`/login?redirect=${to.path}`);
          alert(error || "未知错误");
        }
      }
    }
  } else {
    //未登录
    if (whiteList.indexOf(to.path) !== -1) {
      // 白名单中的路由路过
      next();
    } else {
      // 重定向至登录页
      next(`/login?redirect=${to.path}`);
    }
  }
});

复制代码

添加动态路由


根据用户角色过滤出可访问路由并动态添加到router 创建permission模块,store/modules/permission.js

import { constRoutes, asyncRoutes } from "@/router";

const state = {
  routes: [], //完整路由表
  addRoutes: [] //用户可访问路由表
};

const mutations = {
  SET_ROUTES: (state, routes) => {
    state.addRoutes = routes;
    state.routes = constRoutes.concat(routes);
  }
};

const actions = {
  // 路由生成:在得到用户角色后第一时间调用
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      // 根据角色做过滤处理
      const accessedRoutes = filterAsyncRoutes(asyncRoutes, roles);
      commit("SET_ROUTES", accessedRoutes);
      resolve(accessedRoutes);
    });
  }
};

/**
 * 递归过滤AsyncRoutes路由表
 * @routes 带过滤的路由表,首次传入的就是AsyncRoutes
 * @roles  用户拥有角色
 */
export function filterAsyncRoutes(routes, roles) {
  const res = [];
  routes.forEach(route => {
    // 复制一份
    const tmp = { ...route };
    // 如果用户有访问权限则加入结果路由表
    if (hasPermission(roles, tmp)) {
      // 如果存在子路由则递归过滤之
      if (tmp.children) {
        tmp.children = filterAsyncRoutes(tmp.children, roles);
      }
      res.push(tmp);
    }
  });
  return res;
}

/**
 * 根据路由meta.role确定是否当前用户拥有访问权限
 * @roles 用户拥有的角色
 * @route 待判定路由
 */

export function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    //  若用户拥有的角色中有被包含在待判定的路由角色表中则拥有访问权
    return roles.some(role => route.meta.roles.includes(role));
  } else {
    //  没有设置roles则无需判定即可访问
    return true;
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
};

复制代码

异步获取路由表


用户登录后向后端请求可访问的路由表,从而动态生成可访问页面,操作和原来是相同的,这里多了一步将后端返回路由表中组件名称和本地的组件映射步骤:

//前端的映射表map就是之前的asyncRoutes
//服务端返回的map类似于
const serviceMap = [
	{path:'/login',component:'login',hidden:true}
]
//遍历serviceMap,将component替换为map[component],动态生成asyncRoutes
function mapComponent(serviceMap){
	serviceMap.forEach(route => {
    route.component = map[route.component];
    if(route.children){
    	route.children.map(child => mapComponent(child))
      }
	})
}
mapComponent(serviceMap)
复制代码

按钮权限

封装一个指令v-permission,从而实现按钮级别权限控制,创建src/directtive/permission.js

自定义指令参考 cn.vuejs.org/v2/guide/cu…

import store from "@/store";
const permission = {
  inserted(el, binding) {
    // 获取指令的值:按钮要求的角色数组
    const { value: pRoles } = binding;
    // 获取用户角色
    const roles = store.getters && store.getters.roles;
    if (pRoles && pRoles instanceof Array && pRoles.length > 0) {
      const hasPermission = roles.some(role => {
        return pRoles.includes(role);
      });
      // 如果没有权限删除当前dom
      if (!hasPermission) {
        el.parentNode && el.parentNode.removeChild(el);
      }
    } else {
      throw new Error(
        `需要指定按钮要求角色数组,如v-permission="['admin','editor']"`
      );
    }
  }
};
export default permission;

复制代码

注册指令 main.js

import vPermission from "./directive/permission";
Vue.directive("permission", vPermission);
复制代码

测试

<button v-permission="['admin', 'editor']">admin editor</button>
<button v-permission="['admin']">admin</button>
复制代码

该指令只能删除挂在指令的元素,对于那些额外生成的和指令无关的元素无能为力,比如:挂载在tab上只能删除标签,无法删除对应面板。

可以使用全局权限判断函数,使用v-if实现

<template>
	<el-tab-pane v-if="checkPermission(['admin'])"></el-tab-pane>
</template>
<script>
export default{
	methods:{
    	checkPermission(permissionRoles){
        	return roles.some(role => {
            	return permissionRoles.include(role);
            });
        }
    }
}
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/385140.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

颠覆你的认知,业务同事都能开发软件,我简直无地自容……

经常看到网络鼓吹业务人员也能搭建应用&#xff0c;本是嗤之以鼻、半信半疑&#xff0c;但当这件事真实发生在自己身上时&#xff0c;竟觉得此言不虚&#xff1f; 一、背景 最近公司为了集成系统、提升扩展能力&#xff0c;引进了低代码平台JNPF&#xff0c;说个题外话&#…

终于,OpenAI开放ChatGPT API,成本直降90%,百万token才2美元

现在&#xff0c;第三方可以通过 API 将对话模型 ChatGPT 和语音转文本模型 Whisper 集成到自己的应用程序和服务中了。 来源丨机器之心 2022 年 11 月&#xff0c;OpenAI 上线 ChatGPT&#xff0c;自此以后&#xff0c;这个对话模型一路开挂。毫不夸张的说&#xff0c;与 Ch…

4道数学题,求解极狐GitLab CI 流水线|第4题:合并列车

本文来自&#xff1a; 武让 极狐GitLab 高级解决方案架构师 &#x1f4a1; 极狐GitLab CI 依靠其一体化、轻量化、声明式、开箱即用的特性&#xff0c;在开发者群体中的使用率越来越高&#xff0c;在国内企业中仅次于 Jenkins &#xff0c;排在第二位。 极狐GitLab 流水线有 4…

NFT Insider #87:The Sandbox 收购游戏开发工作室 Sviper,GHST 大迁徙即将拉开帷幕

引言&#xff1a;NFT Insider由NFT收藏组织WHALE Members(https://twitter.com/WHALEMembers)、BeepCrypto&#xff08;https://twitter.com/beep_crypto&#xff09;联合出品&#xff0c;浓缩每周NFT新闻&#xff0c;为大家带来关于NFT最全面、最新鲜、最有价值的讯息。每期周…

洛必达求极限法则的通俗理解

洛必达求极限法则的通俗理解 洛必达法则是用于计算函数在某一点的极限的方法 它的基本思想是利用函数在该点的导数来逼近极限值。 洛必达法则成立的主要原因是因为它是利用函数的导数来逼近函数值的方法。当函数在某一点处存在导数时&#xff0c;函数的变化趋势可以由导数来…

24小时稳定性爆肝测试!国内外5款远程控制软件大盘点

本文目录前言一、ToDesk远程控制二、向日葵远程控制三、RayLink四、TeamViewer五、AnyDesk总结前言 不论你的职业是什么&#xff0c;从事互联网工作基本就离不开远程&#xff0c;从远程安装系统到远程搞设计&#xff0c;再到做服务器的调控&#xff0c;都需要靠远程来协助完成…

如何实现《电子签名法》要求的可靠电子签名?

电子文档的电子签名怎么弄&#xff1f;我们在工作中经常需要在一些Word、pdf等电子版文件中插入签名&#xff0c;而很多人可能不知道&#xff0c;电子签名怎么弄&#xff1f;怎么做电子签名才有效&#xff1f;电子印章或签名图片属于电子签名吗&#xff1f;当工作或商务交易中&…

Typroa安装教程

Markdown 是一种轻量级标记语言&#xff0c;创始人为约翰格鲁伯&#xff08;John Gruber&#xff09;。 它允许人们使用易读易写的纯文本格式编写文档&#xff0c;然后转换成有效的 XHTML&#xff08;或者HTML&#xff09;文档。这种语言吸收了很多在电子邮件中已有的纯文本标记…

中小型企业综合组网及安全配置(附拓扑图和具体实现的代码)

目录 一、实验目的 二、设备与环境 三、实验内容及要求 四、实验命令及结果 五、实验总结 六、实验报告和拓扑图下载链接 一、实验目的 1.了解企业网络建设流程 2.掌握组建中小企业网络的组网技术&#xff1b; 3.掌握组建中小企业网络的安全技术 二、设备与环境 微型…

Nginx国密支持问题记录

文章目录添加国密支持可能出现的问题国密不生效&#xff0c;查看 Nginx 可执行文件路径是否正确证书无法解析Nginx无法启动添加国密支持 NGINX添加国密支持 添加国密支持可以直接按照官网的操作顺序操作即可 参考网址&#xff1a;https://www.gmssl.cn/gmssl/index.jsp 可能出…

【解决】ScrollView 子 Content 在应用 Contentt Size Filter 出现位置自偏移错误问题

开发平台&#xff1a;Unity 2022 开发语言&#xff1a;CSharp 6.0   问题描述 问题表现&#xff1a; Scroll View 出现 Content 的 RectTransform 偏移值会出现自变化情况&#xff0c;但此变化情况不符合预期表现。 问题背景&#xff1a; Scroll View 添加 四周型 适配与 P…

Word控件Spire.Doc 【书签】教程(3): 使用 HTML 代码编辑/替换 Word 书签的内容

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下&#xff0c;轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具&#xff0c;专注于创建、编辑、转…

Python爬虫-阿里翻译_csrf

前言 本文是该专栏的第37篇,后面会持续分享python爬虫干货知识,记得关注。 笔者在前面有介绍过百度翻译的案例,感兴趣的同学,可往前翻阅查看(JS逆向-百度翻译sign)。而本文,笔者要介绍的是阿里翻译,相对于百度翻译的参数被逆向需要花点时间,阿里相对于易上手。 下面…

【java】java消息推送至微信公众号详细教程

文章目录读前必看测试号推送谁说程序员不懂浪漫? 将的关心 推送至微信公众号 给女朋友及时的关怀~&#xff08;这位同学 你女朋友呢&#xff1f;&#xff09; 读前必看 关于微信开发平台&#xff0c;小程序和公众号是不一样的&#xff0c;而公众号又会区分订阅号、服务号、测…

西湖论剑 2022复现

目录 <1> [西湖论剑 2022] Node Magical Login <2> [西湖论剑 2022] real_ez_node(ejs原型链污染&http拆分攻击) <3> [西湖论剑 2022] 扭转乾坤 (RFC差异绕过header头内容限制) <4> [西湖论剑 2022] unusual php(IDA拿rce密钥&利用rce密钥…

DSS 部署环境需求清单

文章目录 DSS系统需求项目地址计算资源计算基准:计算引擎程序硬件需求表 :DSS计算及存储资源需求计算资源计算基准:计算程序硬件需求表:DSS系统需求 项目地址 https://github.com/WeBankFinTech/DataSphereStudio 计算资源计算基准: 1.日活用户10万。 2.单用户单日总…

网络安全怎么学?20年白帽子老江湖告诉你

很多人都知道龙叔是个老程序员&#xff0c;但却不知道其实我也是个H客&#xff0c;20年前我就开始痴迷于H客技术&#xff0c;可以说是网络安全方面的老江湖了。 到现在&#xff0c;我还依然会去研究这一块&#xff0c;偶尔会和一些网安的朋友交流技术&#xff0c;比如说红盟的…

Nginx的三大特点

作为一个 Web 服务器&#xff0c;Nginx 的功能非常完善&#xff0c;完美支持 HTTP/1、HTTPS 和 HTTP/2&#xff0c;而且还在不断进步。 1、进程池 Nginx 作为“轻量级”的服务器&#xff0c;它的 CPU、内存占用都非常少&#xff0c;同样的资源配置下就能够为更多的用户提供服…

Android 绘图基础:Canvas画布——自定义View基础(绘制表盘、矩形、圆形、弧、渐变)

Canvas画布&#xff0c;通过它我们可以自定义一个View&#xff0c;设置View的相关效果之类的。感觉用法差不多&#xff0c;重要的是要理解方法中传入的参数的含义&#xff0c;比如float类型的参数&#xff0c;传递的是坐标,已开是没有注意传入的参数时坐标&#xff0c;导致我迷…

windows编译x265

msys2的安装参考&#xff1a; MSYS2安装_蜡笔小方的博客-CSDN博客 将下载好的x265放入msys2能找到的目录下&#xff1a; 打开msys2&#xff0c;切换到x265/build/msys-cl目录下 其中&#xff0c;build目录下是x265为了各种平台或者不同编译工具配的编译脚本&#xff1f;&…