Еще одно решение для фоновой аутентификации в Vue — динамическая маршрутизация

Vue.js

Случай:vue-element-asyncLogin

2020-2-8 Обновление

Оптимизируйте процесс, упростите логику, удалите файл _import.js и измените его на прямой динамический импорт.

Оптимизировать страницы 404

2020-1-17 Обновление

Обновлен до последней версии шаблона vue-element-template.

Добавить идеи

Оптимизировать часть кода

Увеличьте группу обмена QQ, если у вас есть какие-либо вопросы, добавьте группу

предисловие

Опубликовал статью о Наггетс в начале годаВспомните реализацию маршрутизации динамического рендеринга Vue., теперь код проходит постоянный Review

Теперь предыдущий метод реализации полностью оптимизирован, объем кода значительно уменьшен, логика проще, а также более стабильна.

Демонстрация была размещена на github, добро пожаловать в игру~~vue-element-asyncLogin, Ваш старт - моя мотивация!

Аутентификация-интерфейсная маршрутизация VS Аутентификация-динамическая маршрутизация

Считается, что внешняя маршрутная аутентификация понятна до тех пор, покаvue-element-adminЯ знаю, что план внешней аутентификации вполне осуществим, понятен, умерен и может быть использован в проекте, тогда где преимущество динамической маршрутизации?

  1. Внешняя аутентификация не является гибкой, и онлайн-версию необходимо переупаковывать каждый раз, когда вы изменяете разрешения.
  2. Малые и средние проекты前端鉴权Очевидно, его проще использовать, стоимость ниже, и программистам не нужен 996 (туман), но для проектов со многими уровнями разрешений и относительно больших проектов поддержка этого набора маршрутов аутентификации, несомненно, является большим проектом, и это не легко Для частых изменений ошибки будут появляться чаще, и нагрузка на интерфейсных инженеров значительно возрастет.В настоящее время кажется, что внешняя аутентификация больше не является хорошим решением.
  3. Динамическая маршрутизация — это не возврат к эпохе слэш-энд-бёрна, а новый образ мышления. роли передаются на бэк-энд контроль, а фронт-энду не нужно управлять маршрутизацией, в лучшем случае только проблема гранулярного управления разрешениями

Реализовать идеи

  1. Маршрутный переход Сначала определите, вошли ли вы в систему. Если вы не вошли в систему, вы можете получить доступ только к странице белого списка, а все остальные страницы будут перенаправлены на страницу входа.
  2. Запускается поведение при входе в систему, получается динамическая маршрутизация, информация о динамической маршрутизации рекурсивно анализируется, и в то же время addRouter сохраняется в Vuex, а статус маршрутизации записывается.
  3. Страницы перехода не будут получать динамические маршруты, обновите страницу, чтобы снова получить динамические маршруты

По сравнению с использованием localStorage для хранения статуса входа в систему ранее, статус входа теперь управляется файлами cookie.

Вся информация о маршрутизации передается для управления Vuex, а не из localStorage, что повышает стабильность системы.

Настройка базовой маршрутизации

Конкретные идеи реализации

router/router.js

// ......
// 静态路由
export const StaticRouterMap = [
  {
    path: '/login',
    component: login,
    meta: { title: '管理员登录' },
    hidden: true
  },
  {
    path: '/user',
    component: userLogin,
    redirect: '/user/userlogin',
    name: 'user',
    hidden: true,
    children: [
      {
        path: 'userLogin',
        component: () => import('@/views/userLogin/components/login'),
        meta: { title: '商户登录' }
      },
      {
        path: 'userRegistry',
        component: () => import('@/views/userLogin/components/registry'),
        meta: { title: '商户注册' }
      }
    ]
  },
  {
    path: '/',
    component: Layout,
    redirect: '/dashboard',
    name: 'dashboard',
    children: [
      {
        path: 'dashboard',
        component: () => import('@/views/dashboard/index'),
        meta: { title: '根目录', icon: 'dashboard', affix: true }
      }
    ]
  },
  {
    path: '/404',
    component: () => import('@/views/404'),
    hidden: true
  }
]

export default new Router({
  mode: 'history',
  scrollBehavior: () => ({ y: 0 }),
  routes: StaticRouterMap
})

Настройте структуру маршрутизации с помощью внутренних студентов (json ниже)

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

[{
  id: 1,
  name: 'Example',
  code: null,
  description: null,
  url: '/example',
  component: 'layout',
  generatemenu: 1,
  sort: 0,
  parentId: null,
  permName: null,
  redirect: '/example/table',
  title: '普通用户',
  icon: 'example',
  children: [
    {
      id: 2,
      name: 'Table',
      code: null,
      description: null,
      url: 'table',
      component: 'table',
      generatemenu: 1,
      sort: 0,
      parentId: 1,
      permName: null,
      redirect: '',
      title: 'Table',
      icon: 'table',
      children: null
    },
    {
      id: 3,
      name: 'Tree',
      code: null,
      description: null,
      url: 'tree',
      component: 'tree',
      generatemenu: 1,
      sort: 0,
      parentId: 1,
      permName: null,
      redirect: '',
      title: 'Tree',
      icon: 'tree',
      children: null
    }
  ]
}]

Анализировать начальные данные маршрутизации серверной части как доступные данные

Обработка необработанных данных маршрутизации серверной части

../utils/addRouter

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

Уведомление,Структура маршрутизации соответствует структуре каталогов

/**
 * 生成路由
 * @param {Array} routerlist 格式化路由
 * @returns
 */
export function addRouter(routerlist) {
  const router = []
  try {
    routerlist.forEach(e => {
      let e_new = {
        path: e.url,
        name: e.name,
        component: () => e.component === 'layout' ? import('@/layout') : import(`@/views/${e.component}/index`)
      }
      if (e.children) {
        const children = addRouter(e.children)
        // 保存权限
        e_new = { ...e_new, children: children }
      }
      if (e.redirect) {
        e_new = { ...e_new, redirect: e.redirect }
      }
      if (e.generatemenu === 0) {
        e_new = { ...e_new, hidden: true }
      }
      if (e.icon !== '' && e.title !== '') {
        e_new = { ...e_new, meta: { title: e.title, icon: e.icon } }
      } else if (e.title !== '' && e.icon === '') {
        e_new = { ...e_new, meta: { title: e.title } }
      }
      router.push(e_new)
    })
  } catch (error) {
    console.error(error)
    return []
  }
  return router
}

обработанный маршрут

Обработанный нами маршрут нужно срастить с существующим роутером, а правила обработки маршрутов нужно модифицировать в соответствии с требованиями.

(Основное) Объединение маршрутов

Все вышеперечисленное является подготовительной работой初始路由вернулся с бэкендом动态路由соединение

Эта часть кода также является ядром оптимизации.

import router from './router'
import store from './store'
import user from './store/modules/user'
import { getToken, removeToken } from './utils/auth'
import NProgress from 'nprogress' // Progress 进度条
import 'nprogress/nprogress.css' // Progress 进度条样式
import { Message } from 'element-ui'
import { getRouter } from './api/login'
import { addRouter } from './utils/addRouter'

const whiteList = ['/login']
router.beforeEach((to, from, next) => {
  NProgress.start()
  if (getToken()) {
    // 判断cookice是否存在 不存在即为未登录
    if (to.path !== '/login') {
      if (user.state.init) {
        // 获取了动态路由 data一定true,就无需再次请求 直接放行
        next()
      } else {
        // data为false,一定没有获取动态路由,就跳转到获取动态路由的方法
        gotoRouter(to, next)
      }
    } else {
      Message({ message: '您已经登录', type: 'info' })
      next('/')
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) {
      // 免登陆白名单 直接进入
      next()
    } else {
      if (to.path !== '/login') {
        // 重定向到登录页面 不能这么写 因为假如之前的角色是 管理员页面 后又登陆了非管理员 重定向的页面就可能不存在,就会导致404
        // next(`/login?redirect=${to.path}`)
        next('/login')
      } else {
        next()
      }
    }
  }
})

router.afterEach((to, from) => {
  NProgress.done() // 结束Progress
})

function gotoRouter(to, next) {
  getRouter(store.getters.token) // 获取动态路由的方法
    .then(res => {
      console.log('解析后端动态路由', res)
      const asyncRouter = addRouter(res.data.router) // 进行递归解析
      store.dispatch('user/setroles', res.data.permit)
      return asyncRouter
    })
    .then(asyncRouter => {
      router.addRoutes(asyncRouter) // vue-router提供的addRouter方法进行路由拼接
      console.log(asyncRouter)
      store.dispatch('user/setRouterList', asyncRouter) // 存储到vuex
      store.dispatch('user/GetInfo')
      store.commit('user/set_init', true)
      next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
    })
    .catch(e => {
      console.log(e)
      removeToken()
    })
}

Логика внутри Vuex

import { StaticRouterMap } from '../../router/index'

 state: {
    //.....
    RouterList: [] // 动态路由
 },

mutations: {
    set_router: (state, RouterList) => {
      state.RouterList = RouterList
    }
},

action: {
    // 动态设置路由 此为设置设置途径
    setRouterList({ commit }, routerList) {
      commit('set_router', StaticRouterMap.concat(routerList)) // 进行路由拼接并存储
    },
}

Гораздо проще, чем предыдущая логика

Измените адрес маршрутизации приложения на боковой панели.

Следует отметить, что маршруты, объединенные через addRoutes, не будутthis.$router.options.routesПолучите его, поэтому вам нужно соединить полученный маршрут сthis.$router.options.routesначальство

Наконец, измените код, который отображает часть раздела боковой панели.

src\views\layout\components\Sidebar\index.vue


 computed: {
	// ....
    routes() {
      return this.$store.getters.routerList
    },
   	// ....
  }

Я тщательно подготовил простую демонстрациюvue-element-asyncLogin, добро пожаловать в опыт, если это поможет вам, пожалуйста, не скупитесь на старт~~1