フロントエンドは動的ルーティングを実装します (フロントエンドがすべてのルートを制御し、バックエンドがユーザーの役割を返します)。

ここに画像の説明を挿入

長所と短所

アドバンテージ:

  • ルーティング テーブルはバックエンドの助けを借りずにフロントエンドで維持されます。
  • ロジックは比較的シンプルで使いやすいです
  • フロントエンド認証は、アクセス許可が少ないシステムにとってより便利です

欠点:

  • オンライン版で権限ページを変更するたびに、プロジェクトを再パッケージする必要があります
  • 大規模なプロジェクトには適さない
  • ページにロールを追加し、アクセスできるページを制御する必要がある場合は、フロントエンド認証を使用できません。

具体的なアイデア

1. フロントエンドでスタティックルーティングとダイナミックルーティングを定義し、vue インスタンス作成時に vue-router がスタティックルーティング(ログインなどの権限を必要としないページ)をマウントする 2. ログイン時にユーザー情報を取得し、vuex に保存し
、トークンをストアします。
3. ルーティング時 インターセプターで、トークンを使用してユーザー ロールを取得し、そのロールを使用してアクセス可能なすべてのルートを取得します。 4. router.addrouters(
store.state.addRouters) を呼び出して、アクセス可能なルートを追加します
。 5. ユーザー情報をクリアします。終了時に役割をクリアし、ルーティングをクリアする

ステップ 1: フロントエンドで静的ルーティングと動的ルーティングを定義する

ルーター/index.js

import Vue from "vue"
import VueRouter from "vue-router"
import Layout from "@/layout"

Vue.use(VueRouter)

// 解决重复点击路由报错的BUG
// 下面这段代码主要解决这个问题 :Uncaught (in promise) Error: Redirected when going from "/login" to "/index" via a navigation guard.
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
    
    
  return originalPush.call(this, location).catch((err) => err)
}

// 定义好静态路由
export const constantRoutes = [
  {
    
    
    path: "/login",
    name: "login",
    component: () => import("../views/login"),
    hidden: true,
  },
]

// 定义动态路由,以及每个页面对应的roles(写在meta中,不写代表都可以访问)
export const asyncRoutes = [
  {
    
    
    id: 1,
    name: "/",
    path: "/",
    component: Layout,
    redirect: "/index",
    hidden: false,
    children: [
      {
    
    
        name: "index",
        path: "/index",
        meta: {
    
     title: "index" },
        component: () => import("@/views/index"),
      },
    ],
  },
  {
    
    
    id: 2,
    name: "/form",
    path: "/form",
    component: Layout,
    redirect: "/form/index",
    hidden: false,
    children: [
      {
    
    
        name: "/form/index",
        path: "/form/index",
        meta: {
    
     title: "form" },
        component: () => import("@/views/form"),
      },
    ],
  },
  {
    
    
    id: 3,
    name: "/example",
    path: "/example",
    component: Layout,
    redirect: "/example/tree",
    meta: {
    
     title: "example" },
    hidden: false,
    children: [
      {
    
    
        name: "/tree",
        path: "/example/tree",
        meta: {
    
     title: "tree" },
        component: () => import("@/views/tree"),
      },
      {
    
    
        name: "/copy",
        path: "/example/copy",
        meta: {
    
     title: "copy" },
        component: () => import("@/views/tree/copy"),
      },
    ],
  },
  {
    
    
    id: 4,
    name: "/table",
    path: "/table",
    component: Layout,
    redirect: "/table/index",
    hidden: false,
    meta: {
    
     roles: ["admin"] },
    children: [
      {
    
    
        name: "/table/index",
        path: "/table/index",
        meta: {
    
     title: "table", roles: ["admin"] },
        component: () => import("@/views/table"),
      },
    ],
  },
  {
    
    
    id: 5,
    name: "/admin",
    path: "/admin",
    component: Layout,
    redirect: "/admin/index",
    hidden: false,
    meta: {
    
     roles: ["admin"] },
    children: [
      {
    
    
        name: "/admin/index",
        path: "/admin/index",
        meta: {
    
     title: "admin", roles: ["admin"] },
        component: () => import("@/views/admin"),
      },
    ],
  },
  {
    
    
    id: 6,
    name: "/people",
    path: "/people",
    component: Layout,
    redirect: "/people/index",
    hidden: false,
    meta: {
    
     roles: ["admin", "common_user"] },
    children: [
      {
    
    
        name: "/people/index",
        path: "/people/index",
        meta: {
    
     title: "people", roles: ["admin", "common_user"] },
        component: () => import("@/views/people"),
      },
    ],
  },
  {
    
    
    id: 7,
    name: "/404",
    path: "/404",
    component: () => import("@/views/404"),
  },
  // 注意404页面要放到最后
  {
    
     path: "*", redirect: "/404", hidden: true },
]

const router = new VueRouter({
    
    
  mode: "history",
  base: process.env.BASE_URL,
  routes: constantRoutes,
})

export default router

ここでは、 vue-router によって公式に推奨されている方法に従って、metaタグを使用して、変更されたページがアクセスできる権限を示します。たとえば、meta: { role: ['admin', 'super_editor'] } は、管理者とスーパー編集者のみがこのページに入る資格があることを示します。

注意事项: ここで注意すべき点は、404 ページが最後に読み込まれる必要があるということです。constantRoutes と一緒に 404 を宣言すると、後続のすべてのページが 404 にブロックされます。

ステップ 2: ログイン時にユーザー情報を取得し、vuex に保存し、トークンを保存する

ログイン/index.vue

methods: {
    
    
    login () {
    
    
      this.$refs.userForm.validate((valid) => {
    
    
        if (valid) {
    
    
          // 模拟登录接口去请求用户数据
          setTimeout(() => {
    
    
            // 这里的res就是模拟后台返回的用户数据(不包含用户角色,一般角色是由单独的一个接口返回)
            const res = dynamicUserData.filter((item) => item.username === this.user.username)[0]
            console.log(res)
            // 存储用户的信息及token到vuex,并做sessionStorage持久化处理
            this.$store.commit('User/saveUserInfo', res)
            Message({
    
     type: 'success', message: "登录成功", showClose: true, duration: 3000 })
            this.$router.push({
    
     path: "/index" })
          }, 1000)
        } else return false
      })
    }
  }

添付ファイル: vuex 永続化処理

import Vue from 'vue'
import Vuex from 'vuex'
import User from './modules/user'
import permission from './modules/permission'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)

export default new Vuex.Store({
    
    
  state: {
    
    },
  mutations: {
    
    },
  actions: {
    
    },
  modules: {
    
    
    User,
    permission,
  },
  plugins: [
    createPersistedState({
    
    
      storage: window.sessionStorage, // 可选sessionStorage localStorage
      reducer(val) {
    
    
        return {
    
    
          User: val.User,
        }
      },
    }),
  ],
})

ステップ [34]: ルート インターセプターで、トークンを使用してユーザー ロールを取得し、そのロールを使用してアクセス可能なすべてのルートを取得し、router.addrouters(store.state.addRouters) を呼び出してアクセス可能なルートを追加します。

ルーティングフックロジック:

是否为白名单页面
  是:   直接进入
  不是: 判断是否有token
  			无token:跳转到login登录页
  			有token: 判断用户是否有角色权限表
  						有权限表:直接进入
  						无权限表:调接口获取用户角色,并存储到vuex
  								根据返回的角色和路由表每个页面的需要的权限对比,生成可访问的路由表
  								使用router.addRouters()添加路由

ルーティング ナビゲーション ガード:

import router from "./index"
import NProgress from "nprogress" // progress bar
import store from "@/store"
import menu from "@/mock/menu.js"

NProgress.configure({
    
     showSpinner: false }) // NProgress Configuration

// 白名单页面直接进入
const whiteList = ["/login"]

router.beforeEach((to, from, next) => {
    
    
  NProgress.start()
  // 白名单页面,不管是否有token,是否登录都直接进入
  if (whiteList.indexOf(to.path) !== -1) {
    
    
    next()
    return false
  }
  // 有token(代表了有用户信息,但是不确定有没有角色权限数组)
  if (store.state.User.token) {
    
    
    // 判断当前用户是否有角色权限数组, 是登录状态则一定有路由,直接放行,不是登录状态则去获取路由菜单登录
    // 刷新时hasRoles会重置为false,重新去获取 用户的角色列表
    const hasRoles = store.state.permission.roles && store.state.permission.roles.length > 0
    if (!hasRoles) {
    
    
      setTimeout(async () => {
    
    
        const roles = menu.filter((item) => item.token === store.state.User.token)[0].roles
        // 将该角色权限数组存储到vuex中
        store.commit("permission/setRoles", roles)
        // 根据返回的角色信息去过滤异步路由中该角色可访问的页面
        const accessRoutes = await store.dispatch("permission/generateRoutes", roles)
        // dynamically add accessible routes
        router.addRoutes(accessRoutes)
        // hack方法 router.addRoutes之后的next()可能会失效,因为可能next()的时候路由并没有完全add完成 next(to)解决
        next({
    
     ...to, replace: true })
      }, 500)
    } else {
    
    
      next() //当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的全面会自动进入404页面
    }
  } else {
    
    
    next({
    
     path: "/login" })
  }
})

router.afterEach(() => {
    
    
  // finish progress bar
  NProgress.done()
})

vuex で行うことは次のとおりです。ロール権限配列 (バックグラウンドで返される) を介して定義された動的ルートをフィルタリングし、ユーザーが所有するルートをフィルタリングして除外し、フィルタリングされたルートを静的ルートの後ろに追加して権限を保存します
。 .js

import {
    
     asyncRoutes, constantRoutes } from '@/router'
/**
 * Filter asynchronous routing tables by recursion
 * @param routes asyncRoutes
 * @param roles
 */
export function filterAsyncRoutes(routes, roles) {
    
    
  const res = []
  routes.forEach(route => {
    
    
    const tmp = {
    
     ...route }
    if (hasPermission(roles, tmp)) {
    
    
      if (tmp.children) {
    
    
        tmp.children = filterAsyncRoutes(tmp.children, roles)
      }
      res.push(tmp)
    }
  })
  return res
}

function hasPermission(roles, route) {
    
    
  console.log(roles)
  console.log(route)
  if (route.meta && route.meta.roles) {
    
    
    console.log(roles.some(role => route.meta.roles.includes(role)))
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    
    
    return true
  }
}

const state = {
    
    
  roles: [],
  routes: [],
  addRoutes: [],
}
const mutations = {
    
    
  setRoles(state, val) {
    
    
    state.roles = val
  },
  SET_ROUTES: (state, routes) => {
    
    
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  },
}
const actions = {
    
    
  generateRoutes({
     
      commit }, roles) {
    
    
    return new Promise(resolve => {
    
    
      let accessedRoutes
      if (roles.includes('admin')) {
    
     // admin直接添加所有权限
        accessedRoutes = asyncRoutes || []
      } else {
    
    
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  },
}
export default {
    
    
  namespaced: true,
  state,
  mutations,
  actions,
}

ステップ 4: 終了時にユーザー情報、ロール、ルートをクリアする

methods: {
    
    
    // 退出登录
    handleLogout() {
    
    
      window.localStorage.removeItem("token")
      // 清除用户信息
      this.$store.commit("User/removeUserInfo")
      // 清除角色权限列表
      this.$store.commit("permission/setRoles", [])
      // 清除角色权限数组
      this.$store.commit("permission/SET_ROUTES", [])
      Message({
    
    
        type: "success",
        message: "退出登录",
        showClose: true,
        duration: 3000,
      })
      this.$router.push({
    
     path: "/login" })
    },
  }

お役に立てれば

記事参照元:
フアパンツさん:フアパンツさん
この記事のコード:github for star

おすすめ

転載: blog.csdn.net/weixin_44582045/article/details/132576546