Использовать глобальные охранники маршрута
выполнить
Внешний интерфейс определяет маршрут и помечает соответствующую информацию о разрешении на маршруте.
const routerMap = [
{
path: '/permission',
component: Layout,
redirect: '/permission/index',
alwaysShow: true, // will always show the root menu
meta: {
title: 'permission',
icon: 'lock',
roles: ['admin', 'editor'] // you can set roles in root nav
},
children: [{
path: 'page',
component: () => import('@/views/permission/page'),
name: 'pagePermission',
meta: {
title: 'pagePermission',
roles: ['admin'] // or you can only set roles in sub nav
}
}, {
path: 'directive',
component: () => import('@/views/permission/directive'),
name: 'directivePermission',
meta: {
title: 'directivePermission'
// if do not set roles, means: this page does not require permission
}
}]
}]
Глобальная защита маршрутизации определяет, вошел ли пользователь в систему каждый раз, и пропускает страницу входа, если не вошел в систему. Если вы вошли в систему (вы получили информацию о разрешении пользователя (роль и т. д.), возвращенную из фона), то определите маршрут, на который нужно перейти, и есть ли у пользователя разрешение на доступ (найдите соответствующий маршрут во всех маршрутах в соответствии с к имени маршрута и определить, не является ли пользователь) Иметь информацию о разрешении, отмеченную на маршруте (например, вышеroles: ['admin', 'editor']
)). Если разрешения нет, он перейдет на предопределенный интерфейс (403, 404 и т.п.).
Таким образом, меню может быть сгенерировано непосредственно путем маршрутизации (также будет отображаться меню, для которого у пользователя нет разрешения, и решение о разрешении будет принято, когда пользователь нажмет на переход), или меню может быть сгенерировано путем фильтрации. маршрут в соответствии с разрешением пользователя после входа пользователя (меню необходимо сохранить) в vuex).
В настоящее времяiview-adminВсе еще использую этот путь
недостаток
-
Загрузить все маршруты.Если маршрутов много, а у пользователя нет доступа ко всем маршрутам, это повлияет на производительность.
-
В глобальной защите маршрутизации для каждого перехода маршрутизации требуется определение разрешений.
-
Информация о меню написана на передней панели.Чтобы изменить отображаемый текст или информацию о разрешениях, вам необходимо перекомпилировать
-
Меню связано с маршрутом.При определении маршрута добавляется заголовок меню, значок и другая информация, и маршрут не обязательно отображается в виде меню, но для идентификации добавляются дополнительные поля.
Страница входа отдельно от основного приложения
Ввиду недостатков предыдущей реализации, страницу входа и основное приложение можно разместить на разных страницах (не в одном экземпляре приложения vue).
выполнить
После успешного входа в систему выполните переход на страницу (настоящий переход на страницу, а не переход по маршруту) и передайте разрешения пользователя на страницу, где находится основное приложение.Перед инициализацией основного приложения маршруты фильтруются в соответствии с разрешениями пользователя, а отфильтрованные маршруты используются в качестве экземпляра vue для параметров вместо передачи всех маршрутов, как в предыдущем методе, и нет необходимости делать суждения о разрешениях в глобальной защите маршрутизации.
недостаток
- Нужно делать переходы между страницами, а не чисто одностраничные приложения.
- Информация о меню написана на передней панели.Чтобы изменить отображаемый текст или информацию о разрешениях, вам необходимо перекомпилировать
- Меню связано с маршрутом.При определении маршрута добавляется заголовок меню, значок и другая информация, и маршрут не обязательно отображается в виде меню, но для идентификации добавляются дополнительные поля.
использоватьaddRoutes
Динамически монтировать маршруты
addRoutes
Позволяет динамически монтировать маршруты после инициализации приложения. Благодаря этому новому положению нет необходимости фильтровать маршруты перед инициализацией приложения, как в предыдущем методе.
выполнить
Когда приложение инициализируется, сначала монтируйте маршруты, не требующие контроля разрешений, такие как страницы входа, 404 и другие страницы ошибок.
Существует проблема,addRoutes
Когда и где его вызывать
После входа в систему получите информацию о разрешении пользователя, затем отфильтруйте маршруты, у которых есть разрешение на доступ, а затем вызовитеaddRoutes
Добавьте маршруты. Этот метод осуществим. Однако авторизоваться каждый раз при входе в приложение невозможно, и пользователь обновляет браузер и ему необходимо снова авторизоваться.
такaddRoutes
Или вызовите его в глобальной защите маршрутизации
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css'// progress bar style
import { getToken } from '@/utils/auth' // getToken from cookie
NProgress.configure({ showSpinner: false })// NProgress Configuration
// permission judge function
function hasPermission(roles, permissionRoles) {
if (roles.indexOf('admin') >= 0) return true // admin permission passed directly
if (!permissionRoles) return true
return roles.some(role => permissionRoles.indexOf(role) >= 0)
}
const whiteList = ['/login', '/authredirect']// no redirect whitelist
router.beforeEach((to, from, next) => {
NProgress.start() // start progress bar
if (getToken()) { // determine if there has token
/* has token*/
if (to.path === '/login') {
next({ path: '/' })
NProgress.done() // if current page is dashboard will not trigger afterEach hook, so manually handle it
} else {
if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
store.dispatch('GetUserInfo').then(res => { // 拉取user_info
const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']
store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
}).catch((err) => {
store.dispatch('FedLogOut').then(() => {
Message.error(err || 'Verification failed, please login again')
next({ path: '/' })
})
})
} else {
// 没有动态改变权限的需求可直接next() 删除下方权限判断 ↓
if (hasPermission(store.getters.roles, to.meta.roles)) {
next()//
} else {
next({ path: '/401', replace: true, query: { noGoBack: true }})
}
// 可删 ↑
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
next()
} else {
next('/login') // 否则全部重定向到登录页
NProgress.done() // if current page is login will not trigger afterEach hook, so manually handle it
}
}
})
router.afterEach(() => {
NProgress.done() // finish progress bar
})
Код ключа выглядит следующим образом
if (store.getters.roles.length === 0) { // 判断当前用户是否已拉取完user_info信息
store.dispatch('GetUserInfo').then(res => { // 拉取user_info
const roles = res.data.roles // note: roles must be a array! such as: ['editor','develop']
store.dispatch('GenerateRoutes', { roles }).then(() => { // 根据roles权限生成可访问的路由表
router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
})
}).catch((err) => {
store.dispatch('FedLogOut').then(() => {
Message.error(err || 'Verification failed, please login again')
next({ path: '/' })
})
})
Код вышеvue-element-adminреализация
недостаток
- В глобальной защите маршрутизации необходимо оценивать каждый скачок маршрутизации.
- Информация о меню написана на передней панели.Чтобы изменить отображаемый текст или информацию о разрешениях, вам необходимо перекомпилировать
- Меню связано с маршрутом.При определении маршрута добавляется заголовок меню, значок и другая информация, и маршрут не обязательно отображается в виде меню, но для идентификации добавляются дополнительные поля.
Меню отделяется от маршрута, и меню возвращается бэкендом
Отображаемый заголовок, изображения и т. д. меню необходимо изменить в любое время, и должна выполняться функция управления меню.
Серверная часть напрямую возвращает доступное меню на основе разрешений пользователя.
выполнить
Внешний интерфейс определяет информацию о маршрутизации (стандартное определение маршрутизации, нет необходимости добавлять другие поля тегов).
{
name: "login",
path: "/login",
component: () => import("@/pages/Login.vue")
}
Поле имени не пустое, и оно должно быть связано с внутренним меню возврата в соответствии с этим полем.
При выполнении функции управления меню должно быть поле, соответствующее полю имени внешнего маршрута (это также могут быть другие поля, если меню может найти соответствующий маршрут или маршрут может найти соответствующее меню) , и делаем проверку на уникальность test. В меню также необходимо определить поля разрешений, которых может быть одно или несколько. Другая информация, такая как отображаемый заголовок, значок, сортировка, блокировка и т. д., может быть разработана в соответствии с фактическими потребностями.
Или сделать выводы в глобальной защите маршрутизации
function hasPermission(router, accessMenu) {
if (whiteList.indexOf(router.path) !== -1) {
return true;
}
let menu = Util.getMenuByName(router.name, accessMenu);
if (menu.name) {
return true;
}
return false;
}
Router.beforeEach(async (to, from, next) => {
if (getToken()) {
let userInfo = store.state.user.userInfo;
if (!userInfo.name) {
try {
await store.dispatch("GetUserInfo")
await store.dispatch('updateAccessMenu')
if (to.path === '/login') {
next({ name: 'home_index' })
} else {
//Util.toDefaultPage([...routers], to.name, router, next);
next({ ...to, replace: true })//菜单权限更新完成,重新进一次当前路由
}
}
catch (e) {
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
next()
} else {
next('/login')
}
}
} else {
if (to.path === '/login') {
next({ name: 'home_index' })
} else {
if (hasPermission(to, store.getters.accessMenu)) {
Util.toDefaultPage(store.getters.accessMenu,to, routes, next);
} else {
next({ path: '/403',replace:true })
}
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
next()
} else {
next('/login')
}
}
let menu = Util.getMenuByName(to.name, store.getters.accessMenu);
Util.title(menu.title);
});
Router.afterEach((to) => {
window.scrollTo(0, 0);
});
Приведенный выше кодvue-quasar-adminреализация. потому что не пользуюсьaddRoutes
,Каждый раз при скачке роута надо судить об авторитете.Судить тут тоже очень просто, ибо название меню соответствует названию роута один в один, а возвращаемое бэкендом меню уже отфильтровано уполномоченным органом, поэтому, если в соответствии с Если соответствующее меню не может быть найдено для имени маршрута, это означает, что у пользователя есть разрешение на доступ к нему.
Если маршрутов много, можно монтировать только те маршруты, которые не требуют контроля разрешений при инициализации приложения. После получения меню, возвращенного серверной частью, отфильтруйте доступные маршруты в соответствии с соответствующей связью между меню и маршрутом и передайтеaddRoutes
Динамическое крепление.
недостаток
- Меню должно быть во взаимно однозначном соответствии с роутингом.В интерфейс добавлены новые функции, а новые меню нужно добавлять через функцию управления меню.Если меню настроено неправильно, приложение не будет иметь возможность нормального использования.
- В глобальной защите маршрутизации необходимо оценивать каждый скачок маршрутизации.
Меню и маршруты полностью возвращаются бэкендом
Это работает, когда меню возвращается серверной частью, но как насчет маршрута, возвращаемого серверной частью? Взгляните на определение маршрутизации
{
name: "login",
path: "/login",
component: () => import("@/pages/Login.vue")
}
Если серверная часть возвращается напрямую
{
"name": "login",
"path": "/login",
"component": "() => import('@/pages/Login.vue')"
}
Что это за хрень, явно нет.() => import('@/pages/Login.vue')
Если этот код не отображается во внешнем интерфейсе, веб-пакет не будетLogin.vue
Скомпилировать и упаковать
выполнить
Внешний интерфейс единообразно определяет компоненты маршрутизации, такие как
const Home = () => import("../pages/Home.vue");
const UserInfo = () => import("../pages/UserInfo.vue");
export default {
home: Home,
userInfo: UserInfo
};
Определите компоненты маршрутизации как эту структуру ключ-значение.
Формат возврата бэкенда
[
{
name: "home",
path: "/",
component: "home"
},
{
name: "home",
path: "/userinfo",
component: "userInfo"
}
]
После маршрутизации серверной части обратно черезaddRoutes
Между динамическими монтированиями нужно обрабатывать данные и заменять поле компонента реальным компонентом.
Что касается того, должны ли быть разделены меню и маршрутизация и как они должны соответствовать, это может быть обработано в соответствии с фактическими потребностями.
Если есть вложенные маршруты, при проектировании серверной функции обратите внимание на добавление соответствующих полей. Данные, полученные внешним интерфейсом, также необходимо обрабатывать соответствующим образом.
недостаток
- В глобальной защите маршрутизации необходимо оценивать каждый скачок маршрутизации.
- Передняя и задняя части предъявляют более высокие требования к сотрудничеству
Не используйте глобальные охранники маршрута
предыдущие способы, кроме登录页与主应用分离
, Каждый раз, когда маршрут переключается, глобальная защита маршрутизации принимает решение.
выполнить
При инициализации приложения монтируются только маршруты, не требующие контроля разрешений.
const constRouterMap = [
{
name: "login",
path: "/login",
component: () => import("@/pages/Login.vue")
},
{
path: "/404",
component: () => import("@/pages/Page404.vue")
},
{
path: "/init",
component: () => import("@/pages/Init.vue")
},
{
path: "*",
redirect: "/404"
}
];
export default constRouterMap;
import Vue from "vue";
import Router from "vue-router";
import ConstantRouterMap from "./routers";
Vue.use(Router);
export default new Router({
// mode: 'history', // require service support
scrollBehavior: () => ({ y: 0 }),
routes: ConstantRouterMap
});
Перейти после успешного входа/
маршрутизация
submitForm(formName) {
let _this=this;
this.$refs[formName].validate(valid => {
if (valid) {
_this.$store.dispatch("loginByUserName",{
name:_this.ruleForm2.name,
pass:_this.ruleForm2.pass
}).then(()=>{
_this.$router.push({
path:'/'
})
})
} else {
return false;
}
});
}
Потому что нет тока/
маршрутизация, перейдет к/404
<template>
<h1>404</h1>
</template>
<script>
export default {
name:'page404',
mounted(){
if(!this.$store.state.isLogin){
this.$router.replace({ path: '/login' });
return;
}
if(!this.$store.state.initedApp){
this.$router.replace({ path: '/init' });
return
}
}
}
</script>
В компоненте 404 оценивается, что он был зарегистрирован, а затем оценивается, было ли приложение инициализировано (была ли получена информация о правах пользователя, доступные меню, маршруты и т. д. из бэкэнда). Перейти без инициализации/init
маршрутизация
<template>
<div></div>
</template>
<script>
import { getAccessMenuList } from "../mock/menus";
import components from "../router/routerComponents.js";
export default {
async mounted() {
if (!this.$store.state.isLogin) {
this.$router.push({ path: "/login" });
return;
}
if (!this.$store.state.initedApp) {
const loading = this.$loading({
lock: true,
text: "初始化中",
spinner: "el-icon-loading",
background: "rgba(0, 0, 0, 0.7)"
});
let menus = await getAccessMenuList(); //模拟从后端获取
var routers = [...menus];
for (let router of routers) {
let component = components[router.component];
router.component = component;
}
this.$router.addRoutes(routers);
this.$store.dispatch("setAccessMenuList", menus).then(() => {
loading.close();
this.$router.replace({
path: "/"
});
});
return;
} else {
this.$router.replace({
path: "/"
});
}
}
};
</script>
Определите, было ли приложение инициализировано в компоненте инициализации (чтобы избежать ввода адреса непосредственно из адресной строки, чтобы после инициализации повторно войти в текущий компонент).
Переход, если он уже инициализирован/
Маршрут (если в маршруте, возвращаемом бэкэндом, не определен вторичный маршрут, он перейдет к 404).
Если нет инициализации, вызовите удаленный интерфейс, чтобы получить меню, маршрут и т. д., а затем обработайте маршрут, возвращенный серверной частью, и присвойте компоненту значение true.
компонент, затем вызовитеaddRoutes
Смонтируйте новый маршрут и, наконец, прыгайте/
Просто маршрут. Переработка меню тоже тут, смотрите актуальную
нужно.
недостаток
- Судя по странице 404, это странно.
- Добавлен еще один компонент начальной страницы
Суммировать
Последние две реализации рекомендуются.