vue管理系统权限管理(从后端到前端整个流程) 您所在的位置:网站首页 vue文字按钮怎么做 vue管理系统权限管理(从后端到前端整个流程)

vue管理系统权限管理(从后端到前端整个流程)

2023-06-13 12:24| 来源: 网络整理| 查看: 265

起步

使用vue-cli脚手架安装一个vue3项目(安装时带上路由模块)

前言:做管理系统必定绕不开权限管理这一块,本文详细介绍了对路由权限,接口权限,菜单栏权限,动态路由设置,按钮权限五个模块。码字不易,点赞支持!!!

一、后端设计

在用户登录时,将token,用户等级以及菜单栏列表返回给前端;

token用来判别前端是否登录,用户等级决定前端动态路由,菜单栏列表决定前端菜单栏展示

1. 搭建node服务

在vue3项目中,新建一个server文件夹(与src同级)

新建一个终端,通过命令行cd server进入server文件夹

clipboard.png

运行 npm init -y 初始化packag.json包

安装koa npm i koa -s

新建server/index.js

const Koa = require("koa"); const app = new Koa(); app.use(async (ctx, next) => { ctx.body = "这是一个应用中间件"; await next(); }); app.listen(4000, () => { console.log("server is listening on port 4000"); });

5. 安装nodemon npm i nodemon -g

修改server/package.json "scripts": { "dev": "nodemon index.js", "test": "echo \"Error: no test specified\" && exit 1" },

运行:npm run dev 访问:127.0.0.1:4000,可以看到页面显示这是一个应用中间件

2. 使用路由中间件

安装 npm i koa-router -S

新建server/routes/index.js

const router = require("koa-router")(); let accessToken = "init_s_token"; //定义token let role = ""; //定义用户等级 let menus = []; //定义菜单列表 /* 5s刷新一次token */ setInterval(() => { accessToken = "s_tk" + Math.random(); }, 5000); /* 登录接口获取token */ router.get("/login", async (ctx) => { const { name } = ctx.query; switch (name) { case "admin": role = "admin"; menus = ["home", "about", "manage"]; //管理员能看到首页,说明页和管理页 break; default: role = "visitor"; menus = ["home", "about"]; //游客只能看到首页,说明页 break; } ctx.body = { accessToken, role, menus, }; }); /* 获取应用数据 */ router.get("/getData", async (ctx) => { let { authorization } = ctx.headers; if (authorization !== accessToken) { ctx.body = { returncode: 104, info: "token过期,重新登录", }; } else { ctx.body = { code: 200, returncode: 0, data: { id: Math.random() }, }; } }); module.exports = router;

3. 修改server/index.js

//删除 app.use(async (ctx, next) => { ctx.body = "这是一个应用中间件"; await next(); }); //新增 const index = require("./routes/index"); app.use(index.routes(), index.allowedMethods()); 3. 跨域处理

安装 npm i koa2-cors

修改server/index.js

//新增 const cors = require("koa2-cors"); app.use(cors());

最终server/index.js文件

const Koa = require("koa"); const app = new Koa(); const index = require("./routes/index"); const cors = require("koa2-cors"); app.use(cors()); app.use(index.routes(), index.allowedMethods()); app.listen(4000, () => { console.log("server is listening on port 4000"); });

目录结构:

995B30E73E374C6A8CAE79809B8ADD6A.jpg

重新运行 npm run dev,这时服务端已准备好

从截图中可以看出起了两个服务,一个前端本地服务,一个node服务

二、前端设计

技术准备:

定义使用到的常量

新建src/config/constant.js

//localStorage存储字段 export const ACCESS_TOKEN = "tk"; //存token export const ROLE = 'role'; //存用户等级 export const MENUS = 'menus';//存菜单列表 //HTTP请求头字段 export const AUTH = "Authorization";

新建src/config/returnCodeMap.js

//接口状态码 export const CODE_LOGGED_OTHER = 106;// 在其它客户端被登录 export const CODE_RELOGIN = 104;// 重新登陆

新建src/config/menus.js

const menus = [ { path: "/home", key: "home", name: "首页", }, { path: "/about", key: "about", name: "说明页", }, { path: "/manage", key: "manage", name: "管理页", }, ]; export default menus

2. 安装axios服务

安装 npm i axios

新建src/service/index.js

//axios服务 import axios from "axios"; const service = axios.create({ baseURL: "//127.0.0.1:4000", timeout: 30000, }); export default service;

新建src/service/api.js

//定义接口 import service from "./index"; const API = {}; /* 登录接口 */ API.getLogin = (params) => { return service.get("/login", { params: params }); }; /* 获取应用数据接口 */ API.getData = () => { return service.get("/getData"); }; export default API;

3. 调整目录结构

一个管理系统需要登录页,登录之后使用嵌套路由,layout用来布局,展示左侧菜单栏和头部用户信息,右侧用来展示页面内容,这样在路由切换时,菜单栏和头部可以保持不变

clipboard.png

新建src/Layout.vue,src/views/Login.vue,src/views/Manage.vue(管理员才能访问的页面),还有创建项目自带的About.vue和Home.vue

调整路由

新建src/router/routes.js

//配置路由 const Login = () => import(/* webpackChunkName: "login" */ "../views/Login.vue"); const Home = () => import(/* webpackChunkName: "home" */ "../views/Home.vue"); const About = () => import(/* webpackChunkName: "about"*/ "../views/About.vue") const Layout = () => import(/* webpackChunkName: "layout" */ "../Layout.vue"); const routes = [ { path: "/", redirect: "/home", }, { path: "/login", component: Login, }, { path: "/layout", name: "Layout", component: Layout, children: [ { path: "/home", name: "Home", component: Home, //存放按钮权限信息 meta: { btnPermissions: ['admin', 'visitor'] }, }, { path: "/about", name: "About", component: About, meta: { btnPermissions: ['admin'] }, }, ], }, ]; export default routes;

修改src/router/index.js

import { createRouter, createWebHistory } from "vue-router"; import routes from "./routes"; const router = createRouter({ history: createWebHistory(), routes, }); export default router;

新建src/router/dynamicRoute.js

//动态路由 const manage = { path: "/manage", name: "manage", component: () => import(/* webpackChunkName:"manage" */ "../views/Manage.vue"), }; export default manage

新建终端,启动前端服务。这时页面能出来了,接下来进行权限管理

1. 路由权限设置

情景:当用户没有登录,直接访问页面时,重定向到登录页登录

思路:在路由全局前置钩子中,增加鉴权功能

修改src/router/index.js

import { ACCESS_TOKEN, ROLE, MENUS } from "../config/constant"; router.beforeEach((to, from, next) => { if (to.path === "/login") { //在登录页清除存储信息 localStorage.removeItem(ACCESS_TOKEN); localStorage.removeItem(ROLE); localStorage.removeItem(MENUS); } let token = localStorage.getItem(ACCESS_TOKEN); //没有token,则重定向到登录页 if (!token && to.path !== "/login") { next({ path: "/login", }); } else { next(); } }); 2. 接口权限设置

情景:当token过期时,需用户重新登录

思路:在请求拦截器中,将token添加到请求头中;在响应拦截器中,判断状态码决定是否跳转到登录页

增加请求拦截器和响应拦截器

修改src/service/index.js

//新增 import { CODE_LOGGED_OTHER, CODE_RELOGIN } from "../config/returnCodeMap"; import { ACCESS_TOKEN, AUTH } from "../config/constant"; import router from "../router"; service.interceptors.request.use( (config) => { let { headers } = config; const tk = localStorage.getItem(ACCESS_TOKEN); tk && Object.assign(headers, { [AUTH]: tk, }); return config; }, (error) => { return Promise.reject(error); } ); service.interceptors.response.use( (res) => { let { data } = res; if ( data.returncode === CODE_RELOGIN || data.returncode === CODE_LOGGED_OTHER ) { router.push("/login"); //清除动态路由缓存 location.reload(); } return res; }, (error) => { return Promise.reject(error); } );

2. 登录页

修改src/views/Login.vue

XXXX管理系统 姓名: 密码: 提交 import { reactive } from "vue"; import { useRouter } from "vue-router"; import API from "../service/api"; import { ACCESS_TOKEN, ROLE, MENUS } from "../config/constant"; import manageRoute from "../router/dynamicRoute"; export default { setup() { const router = useRouter(); const user = reactive({ name: "", password: "" }); const sumbit = () => { API.getLogin(user).then((res) => { localStorage.setItem(ACCESS_TOKEN, res.data.accessToken); localStorage.setItem(ROLE, res.data.role); localStorage.setItem(MENUS, JSON.stringify(res.data.menus)); if (res.data.role === "admin") { router.addRoute("Layout", manageRoute); } router.push("/home"); }); }; return { user, sumbit }; }, };

3. 首页

修改src/views/Home.vue

Home页面 提交 import { reactive } from "vue"; import API from "../service/api"; export default { setup() { const user = reactive({ name: "", password: "" }); const sumbit = () => { console.log(user); API.getData('/getData') }; return { user, sumbit }; }, }

访问http://127.0.0.1:8080/login 进入登录页,登录之后过5s在首页点击获取数据按钮(token在服务端上设置了5s的过期时间),后端判断token是否过期,过期返回过期状态码,响应拦截器根据状态码跳转到登录页

3. 菜单栏权限设置

情景:不同级别用户看到不同菜单栏

思路:前端通过返回的菜单栏列表,去封装一个新的菜单栏数组

修改src/Layout.vue

退出 {{item.name}} import menus from "./config/menus"; import { MENUS } from "./config/constant"; export default { data() { return { newMenus:[] }; }, created() { const menuKeys = JSON.parse(localStorage.getItem(MENUS)); menus.forEach((item) => { if (item.key && menuKeys.includes(item.key)) this.newMenus.push(item); }); }, methods: { exit() { this.$router.push("/login"); //清除动态路由缓存 location.reload(); }, }, }; #home { height: 100vh; } header { background: #f4f4f5; height: 70px; } main { display: flex; height: 100%; } aside { width: 150px; background: gray; height: 100%; } article { flex: 1; }

这时通过用户名为 admin 的账户登录能看到三个菜单,其他用户只能看到两个

4. 动态路由设置

情景:管理员能访问管理页面路由,非管理员不能访问该路由

思路:通过router.addRoute添加动态路由

修改src/App.vue body { margin: 0; }

2. 管理员页面

修改src/views/Manage.vue

管理员才能看到的页面

3. 添加动态路由

在src/views/Login.vue中新增(上面Login.vue文件已经加上了,这里单独拎出来展示)

import manageRoute from '../router/manageRoute' //如果是管理员,添加管理员页面路由 if (res.data.role === "admin") { router.addRoute("Layout", manageRoute); }

提示:这里用的是vue3,对应的vue-router是4.x版本,使用addRoute添加动态路由。vue2对应的vue-router是3.x版本,使用addRoutes添加动态路由

解决刷新页面,动态路由丢失

原因:刷新页面,路由初始化,动态路由会丢失

思路:通过监听路由的变化,当刷新时,添加动态路由并定位到管理页面

修改src/App.vue

//新增 import { ROLE } from "./config/constant"; import manage from "./router/dynamicRoute"; export default { watch: { $route: { async handler(newVal) { console.log("newVal", newVal); const role = localStorage.getItem(ROLE); if (role && role === "admin") { /* 在4.x版本中需手动调用router.replace方法重定向, 因为动态路由页面刷新时,matched的值为空; 在3.x版本中,刷新页面添加异步路由,matched有值,不需要再重定向 */ this.$router.addRoute("Layout", manage); /* 在动态路由页面刷新时,matched数组为空 */ if (!newVal.matched.length && newVal.fullPath === "/manage") { await this.$router.replace("/manage"); } } }, }, }, }; 5. 按钮权限设置

情景:根据不同的用户,一些页面功能进行显示或者隐藏

思路:在路由元信息上定义权限信息,通过自定义指令删除一些DOM节点

定义路由元信息(上面routes.js中已经添加了) { path: "/about", name: "About", component: About, meta: { btnPermissions: ['admin'] }, },

2. 增加判断方法

新建src/utils/index.js

import { ROLE } from "../config/constant"; // 权限检查方法 export function has(value) { let isExist = false; // 获取用户按钮权限 let btnPermissionsStr = localStorage.getItem(ROLE); if (btnPermissionsStr == undefined || btnPermissionsStr == null) { return false; } if (value.indexOf(btnPermissionsStr) > -1) { isExist = true; } return isExist; }

3. 新建自定义指令

修改src/main.js

import { createApp } from "vue"; import App from "./App.vue"; import router from "./router"; import { has } from "./utils"; const app = createApp(App); app.directive("has", { mounted(el) { // 获取页面按钮权限 const btnPermissionsArr = router.currentRoute._value.meta.btnPermissions; if (!has(btnPermissionsArr)) { if (el.parentNode) { el.parentNode.removeChild(el); } } }, }); app.use(router).mount("#app");

4. 在about页面使用v-has指令

修改src/views/About.vue

This is an about page 管理员按钮

效果演示:

1.gif

欢迎关注:之后文章会首发在云在前端公众号,未经许可禁止转载!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有