后端控制权限
思路:用户登录后可以获取到用户登陆的菜单,在跳转到下一个页面前,将获取到的用户的菜单格式化一下,以满足前端的规则,然后通过addRoute方法把新的路由加进去,然后记录路由规则的state,方便后续使用。
模拟后端接口
前端请求/routers接口时传入的用户id,后端返回相应的(该用户有权限的)页面,返回数据大概如下:
前端访问/login接口传入账户密码,然后返回相应的id,代码大概如下:
前端请求逻辑
route/index里面的路由规则是基本的路由规则,是不用登录也能访问的页面。perssion.js里面的路由规则需要登录才能访问,需要根据权限来判断能不能访问
登陆
将用户输入的账号密码传给后端,使用js-cookie库的Cookies.set
将后端返回的token保存在cookie中,views/login.vue:
<template><div class="home"><input v-model="username" /><input v-model="password" /><button @click="loginIn">登录</button></div>
</template><script>
import axios from "axios"
import Cookies from 'js-cookie'export default {name: 'login',data() {return {username: "",password: ""}},methods: {loginIn() {axios.post("http://localhost:3000/login",{ username: this.username, password: this.password }).then((res) => {if (res.data.token) {Cookies.set('token', res.data.token);Cookies.set('id', res.data.id);this.$router.push("/");}})}}
}
</script>
建议将路由规则保存在store里,因为首页渲染菜单需要用到。
思路:在actions中发请求获取路由规则,然后解析并格式化路由规则,在mutation中修改路由。store/index.js:
import Vue from 'vue'
import Vuex from 'vuex'
import Cookies from 'js-cookie'
import { initRoutes, resetRouter } from "../router"
import axios from 'axios'
Vue.use(Vuex)export default new Vuex.Store({state: {asyncRoute: [],// 请求回来的路由routes: []// 所有的路由(初始路由+请求回来的路由)},mutations: {SET_ROUTES: (state, routes) => {state.asyncRoute = routes;state.routes = initRoutes.concat(routes);// 初始路由拼接请求回来的路由,获得完整路由},RESET_ROUTES: (state, routes) => {state.asyncRoute = [];state.routes = [];},},actions: {// 发请求获取路由规则,然后解析并格式化路由规则,返回出去供前端使用getRouter({ commit }) {// routeArr为路由规则数组,就是访问/routers得到的响应数组function parseRouter(routeArr) {let _newArr = [];routeArr.forEach((item) => {let _newItem = Object.assign({}, item);let _str = item.component;//"/page1.vue"=>import("@/views/page1.vue")//格式化规则_newItem.component = (resolve) => {//两个坑//return require([`@/views${_str}`],resolve)// 1. 有些低版本的webpack不能使用import,得用requirereturn import(`@/views${_str}`)// 2. 这里必须用模版引擎,并且后端返回不能返回src/views,如果带上了,这里写`@${_str}`也会报错}_newArr.push(_newItem);})return _newArr}let _id = Cookies.get("id")if (_id) {return new Promise((resolve, reject) => {// 每次刷新页面都会发请求,所以每次路由跳转前先检查localStorage中有没有路由规则,有则直接使用,没有再发请求获取,并将请求到的路由规则保存在localStorage中let _local = JSON.parse(localStorage.getItem("menu"));if (_local) {let _newArr = parseRouter(_local);commit("SET_ROUTES", _newArr);resolve(_newArr)} else {axios.get("http://localhost:3000/routes?id=" + _id).then((res) => {let _newArr = parseRouter(res.data.data)localStorage.setItem("menu", JSON.stringify(res.data.data));commit("SET_ROUTES", _newArr);resolve(_newArr);})}})}},//按钮//登录-拉去一下用户的权限code ["some1","SOMETHING"]getCode() {},resetRouter({ commit }) {resetRouter();commit('RESET_ROUTES');}},modules: {}
})
router/index.js:
import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import login from '../views/login.vue'
Vue.use(VueRouter)
export const initRoutes = [{path: '/',name: 'Home',component: Home,meta: {// 可以访问这个页面的用户,用户登陆后,前端可以通过登陆接口获取到用户,如果是admin/manger用户,在beforeEach中通过to.meta.whiteList判断当前用户有没有权限,// 如果有权限,to.next()whiteList: ["admin", "manger"]}},{path: '/about',name: 'About',// route level code-splitting// this generates a separate chunk (about.[hash].js) for this route// which is lazy-loaded when the route is visited.component: function () {return import(/* webpackChunkName: "about" */ '../views/About.vue')}},{path: '/login',name: 'login',component: login},
]const router = new VueRouter({routes: initRoutes
})// 重置路由规则:创建新的路由,把新路由的规则给老路由
export function resetRouter() {const newrouter = new VueRouter({routes: initRoutes})router.matcher = newrouter.matcher
}
export default router
权限控制页面perssion.js
import router from './router'
import store from './store'
import axios from "axios"
import Cookies from 'js-cookie'
//根据id请求接口获取规则
let whiteList = ["/about", "/login"]// 不需要登陆就可以访问的页面
// 因为store.dispatch("getRouter")会返回一个promise,需要await一下,等返回后再操作,所以这里得写成async函数
router.beforeEach(async (to, from, next) => {const token = Cookies.get("token");if (token) {if (to.path == "/login") {next("/");} else {//判断是不是已经请求拿了路由规则了if (store.state.asyncRoute.length == 0) {const _asyncRoutes = await store.dispatch("getRouter");// 获取到路由规则_asyncRoutes.forEach((item) => {router.addRoute(item);//注册路由})//继续跳转next(to.path)} else {//没有page3 page4//user page1 page2 if (to.matched.length != 0) {// 要去的这个路由在路由规则中有没有匹配next();} else {alert("无页面权限")next(from.path);}}}} else {if (whiteList.indexOf(to.path) != -1) {next()} else {next("/login")}}
})
登陆页:
<template><div class="home"><input v-model="username" /><input v-model="password" /><button @click="loginIn">登录</button></div>
</template><script>
import axios from "axios"
import Cookies from 'js-cookie'export default {name: 'login',data() {return {username: "",password: ""}},methods: {loginIn() {axios.post("http://localhost:3000/login",{ username: this.username, password: this.password }).then((res) => {if (res.data.token) {Cookies.set('token', res.data.token);Cookies.set('id', res.data.id);this.$router.push("/");}})}}
}
</script>
登出页:
<template><div class="home"><div v-for="item in $store.state.routes"><router-link :to="item.path">{{ item.name }}</router-link></div><button @click="loginout">登出</button></div>
</template><script>
import Cookie from "js-cookie"
export default {name: 'home',created() {},methods: {loginout() {// 退出登陆时清空cookie,清空localStorage、store,清空路由,跳转到登陆页Cookie.remove("id");Cookie.remove("token");localStorage.removeItem("menu");this.$store.dispatch("resetRouter")this.$router.push("/login")}}
}
</script>
前端路由和按钮权限控制
按钮权限思路:登陆时拉取用户权限code,每个code都有对应的操作权限,如果按钮有某个操作权限,则展示,否则隐藏
路由思路:为路由定义meta属性,在其中定义whiteList对象,存放可以访问这个页面的用户,用户登陆后,前端可以通过登陆接口获取到用户,如果是admin/manger用户,在beforeEach中通过to.meta.whiteList判断当前用户有没有权限,如果有权限:to.next()
export const initRoutes = [{path: '/',name: 'Home',component: Home,meta: {// 可以访问这个页面的用户,用户登陆后,前端可以通过登陆接口获取到用户,如果是admin/manger用户,在beforeEach中通过to.meta.whiteList判断当前用户有没有权限,// 如果有权限,to.next()whiteList: ["admin", "manger"]}},]