Аутентификация входа в проект Vue + динамическая маршрутизация

        Система имеет несколько ролей, и разные роли должны иметь разные таблицы маршрутизации после входа в систему, чтобы гарантировать, что пользователи не получат доступ к правильному адресу, если они введут случайные URL-адреса, а перейдут на страницу 404. Использование токена для аутентификации здесь в основном предназначено для предотвращения атак csrf.

1. CSRF-атака

        Подделка межсайтовых сценариев заключается в том, чтобы побудить пользователей щелкнуть опасные веб-сайты после входа на безопасный веб-сайт, после чего опасный веб-сайт может получить файл cookie пользователя и выдать себя за личность пользователя, чтобы посетить безопасный веб-сайт и выполнить некоторые незаконные операции.

Тогда ключевой момент:

  • Войдите на доверенный сайт А и сгенерируйте файл cookie локально;
  • Посетите вредоносный сайт B, не выходя из системы на сайте A (удалив файлы cookie сайта A).

Поэтому один из способов предотвратить это — использовать токен, который невозможно перехватить.

Затем используйте токен для проверки здесь, разделенного на наличие токена и отсутствие токена.

router.beforeEach((to, from, next) => {
  if (!getToken()) next({ name: 'Login' })
  else next()
})

2. Добавьте новую роль с текущими разрешениями в мета данных маршрутизации.

routes: [
    {
        path: '/login',
        name: 'login',
        meta: {
            roles: ['admin', 'user']
        },
        component: () => import('../components/Login.vue')
    },
    {
        path: 'home',
        name: 'home',
        meta: {
            roles: ['admin']
        },
        component: () => import('../views/Home.vue')
    },
]

Затем проверьте, отображается ли маршрут в router.beforeEach.

//假设有三种角色:admin 和 user 和 manager
//从后台获取的用户角色,一个用户可能有多个角色
const role = [admin, user]  //从localStorage中获取
//当进入一个页面是会触发导航守卫 router.beforeEach 事件
router.beforeEach((to, from, next) => {
    const auth = role.filter((item) => to.meta.roles.includes(item));  //两个数组有交集,就放行
    if (auth.length) {
        next() //放行
    } else {
        next({path:"/404"}) //跳到404页面
    }
})

Проблема: при использовании router.beforeEach обнаружено, что обновление страницы не запускает router.beforeEach.

Решение. Порядок привязки маршрутизатора к новому приложению Vue должен быть указан в конце файла main.js, то есть сначала должна быть написана функция router.beforeEach, а затем маршрутизатор привязывается к корневому экземпляру Vue.

3. По умолчанию в route.js есть только маршруты входа и регистрации.

        По умолчанию в route.js есть только маршруты страницы входа и регистрации, а остальные маршруты добавляются через addRouter vue-router.Следует отметить, что динамические добавленные маршруты являются push-маршрутами в таблице маршрутизации, потому что маршруты совпадают по порядку, поэтому такие маршруты, как страницы 404, необходимо размещать в конце динамического добавления.

// dynamicRoutes.js
// 将需要动态注册的路由提取到vuex中
const dynamicRoutes = [
    {
        path: '/manage',
        name: 'Manage',
        meta: {
            requireAuth: true
        },
        component: () => import('./views/Manage')
    },
    {
        path: '/userCenter',
        name: 'UserCenter',
        meta: {
            requireAuth: true
        },
        component: () => import('./views/UserCenter')
    }
]

После входа в систему, перед скачками маршрутизации, вызывается интерфейс для получения таблицы полномочий маршрутизации роли текущего пользователя.

Сохраните данные таблицы в маршрутизаторах sessionStorage, а затем выполните двусторонний глубокий рекурсивный обход с помощью dynamicRoutes, который будет введен выше, чтобы отфильтровать авторизованные маршруты.

Затем this.$router.addRoutes (отфильтрованный маршрут), а затем перейти к маршруту.

После такого входа в систему проблем с обычными переходами маршрутизации нет, но это закончится, как только он будет обновлен, потому что router.js будет переинициализирован после обновления, и таблица динамической маршрутизации исчезнет. В это время будут работать маршрутизаторы, только что сохраненные в sessionStorage.Изначально я планировал добавить обновленный динамический маршрут в созданный корневой каталог, но позже обнаружил, что это невозможно. Поэтому я помещаю этот уровень суждений непосредственно в main.js.

// main.js
import dynamicRoutes from '../routes/dynamicRoutes.js';
import router from 'vue-router'
let localRoutes = sessionStorage.getItem('routers');
if (localRoutes) {
    const route =  dynamicRoutes.filter((item) => localRoutes.includes(item)) //多层级路由要递归遍历;
    router.addRoutes(route);
}

Таким образом, данные в sessionStorage также можно получить, обновив страницу. Место хранения здесь тоже можно разместить в vuex, но у vuex тоже есть проблема потери данных при обновлении страницы, принцип тот же, что и выше.

4. Пример аутентификации и маршрутизации

import Vue from 'vue'
import $cookies from 'vue-cookies'
import VueRouter from 'vue-router'
import store from '../store'​
import staticRoute from './static-route.js'​​​
Vue.use(VueRouter)​​
const router = new VueRouter({
	mode: 'history',
	base: process.env.BASE_URL,
	routes: staticRoute //staticRoute为静态路由,不需动态添加
})​
let isToken = true
router.beforeEach(async (to, from, next) => {
	//定义isToken为true和vuex不为空时添加路由
	if (isToken && store.state.routers.routers.length != 0) {
		//从vuex中获取动态路由
		const accessRouteses = await store.state.routers.routers;
		//动态路由循环解析和添加
		accessRouteses.forEach(v => {
			v.children = routerChildren(v.children);
			v.component = routerCom(v.component);
			router.addRoute(v); //添加
		})
		isToken = false //将isToken赋为 false ,否则会一直循环,崩溃
		next({
			...to, // next({ ...to })的目的,是保证路由添加完了再进入页面 (可以理解为重进一次)
			replace: true, // 重进一次, 不保留重复历史
		})​
	} else {
		if (to.name == null) {
			next("/404")
		} else {
			if (to.meta.title) { //判断是否有标题
				document.title = to.meta.title //给相应页面添加标题
			}
			next()
		}​
	}​
})​
function routerCom(path) { //对路由的component解析
	return (resolve) => require([`@/views/${path}`], resolve);
}​
function routerChildren(children) { //对子路由的component解析
	children.forEach(v => {
		v.component = routerCom(v.component);
		if (v.children != undefined) {
			v.children = routerChildren(v.children)
		}
	})
	return children;
}​​
export default router​;

Guess you like

Origin blog.csdn.net/qq_24518001/article/details/127955020