【vue项目权限控制方案】
后端控制权限
思路:用户登录后可以获取到用户登陆的菜单,在跳转到下一个页面前,将获取到的用户的菜单格式化一下,以满足前端的规则,然后通过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,得用require
return 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"]
}
},
]