最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

vue-router 控制路由權(quán)限的實現(xiàn)

 更新時間:2020年09月24日 09:28:37   作者:__此間少年  
這篇文章主要介紹了vue-router 控制路由權(quán)限的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

注意:vue-router是無法完全控制前端路由權(quán)限。

1、實現(xiàn)思路

使用vue-router實例函數(shù)addRoutes動態(tài)添加路由規(guī)則,不多廢話直接上思維導圖:

2、實現(xiàn)步驟

2.1、路由匹配判斷

// src/router.js

import Vue from 'vue';
import Store from '@/store';
import Router from 'vue-router';
import Cookie from 'js-cookie';
const routers = new Router({
  base : "/test",
  // 定義默認路由比如登錄、404、401等
  routes : [{
    path : "/404",
    // ...
  },{
    path : "/401",
    // ...
  }]
})
// ...省略部分代碼
routes.beforeEach((to, from, next) => {
  const { meta, matched, path } = to;
  let isMatched = matched && matched.length > 0; // 是否匹配路由
  if(isMatched){
  
  }else{
  
  }
})

通過vue-router前置守衛(wèi)beforeEach中參數(shù)to來簡單的實現(xiàn)匹配結(jié)果

2.2、登錄訪問控制

在實際開發(fā)中路由常常存在是否登錄訪問和是否需要登錄訪問的情況,于是可以通過token和路由配置meta信息中定義isAuth字段來區(qū)分。

// ...省略部分重復代碼

const openRouters = [];
const authRouters = [{
  path : "order/list",
  // ...
  meta : {
    // 是否身份驗證(至于默認定義false還是true由開發(fā)者自定義)
    isAuth : true
  }
}];

routes.beforeEach((to, from, next) => {
  const { meta, matched, path } = to;
  let isMatched = matched && matched.length > 0; // 是否匹配路由
  let isLogin = Cookie.get("token") || null;
  let { isAuth } = (meta || {});
  if(isMatched){
    // 匹配到路由
    if(isAuth){
      // 需要登錄訪問
      if(isLogin){
        // 已登錄訪問
        next(); // 調(diào)用鉤子函數(shù)
      }else{
        // 未登錄訪問
        next("/login"); // 跳轉(zhuǎn)登錄
      }
    }else{
      // 不需要登錄訪問
      next(); // 調(diào)用鉤子函數(shù)
    }
  }else{
    // 未匹配到路由
    if(isLogin){
      // 已登錄訪問
      
    }else{
      // 未登錄訪問
      next("/login"); // 跳轉(zhuǎn)登錄
    }
  }
})

2.3、動態(tài)添加路由規(guī)則

實現(xiàn)動態(tài)添加路由規(guī)則只需要使用vue-router實例方法router.addRoutes(routes: Array) 。
那么問題來了,我們怎么才能獲取到需要動態(tài)添加的路由規(guī)則呢?

2.4、構(gòu)建路由規(guī)則匹配函數(shù)

假如后臺獲取到的路由權(quán)限列表是這樣的:

[{
 resourceUrl : "/order/list",
 childMenu : ...
}]

為了對比用戶權(quán)限和路由是否匹配我們需要提取出權(quán)限路由數(shù)組

// 簡單的通過遞歸獲取到了所有權(quán)限url
export function getAuthRouters(authMenu) {
  let authRouters = [];
  (authMenu || []).forEach((item) => {
    const { resourceUrl, childMenu } = item;
    resourceUrl && authRouters.push(resourceUrl);
    if (childMenu && childMenu.length > 0) {
      // 合并子級菜單
      authRouters = [...authRouters, ...getAuthRouters(childMenu)];
    }
  });
  return authRouters;
}

通過getAuthRouters函數(shù)獲取到了所有用戶路由權(quán)限,接下來是要怎么和vue-router路由匹配呢?

這要和(我這里使用的是RBAC模型)系統(tǒng)配置權(quán)限關聯(lián)上。vue-router路由規(guī)則要和權(quán)限配置保持一致。所以通過遞歸動態(tài)拼接vue-router路由規(guī)則和用戶擁有的路由權(quán)限做對比。如果匹配就保留該路由;然后得到一份過濾后的vue-router路由規(guī)則配置。最后通過實例方法addRoutes添加路由規(guī)則。具體實現(xiàn)代碼如下:

// src/utils/index.js
const { pathToRegexp } = require('path-to-regexp');

export function createAuthRouters(authRouters) {
  const isAuthUrl = (url) => {
    return (authRouters || []).some((cUrl) => {
      return pathToRegexp(url).toString() === pathToRegexp(cUrl).toString();
    });
  };
  return function createRouters(routers, upperPath) {
    let nRouters = [];
    (routers || []).forEach((item) => {
      const { children, path, name } = item;
      let isMatched = false,
        nItem = { ...item },
        fullPath = `${upperPath || ''}/${path}`.replace(/\/{2,}/, '/'),
        nChildren = null;
      children && (nChildren = createRouters(children, fullPath));
      // 1.當前路由匹配
      if (isAuthUrl(fullPath)) {
        isMatched = true;
      }
      // 2.存在子路由匹配
      if (nChildren && nChildren.length > 0) {
        nItem.children = nChildren;
        isMatched = true;
      }
      // 特殊處理(不需要可以刪除)
      if(name === "home"){
        isMatched = true;
      }
      // nItem
      isMatched && nRouters.push(nItem);
    });
    return nRouters;
  };
}

值得注意的是createAuthRouters方法通過變量isMatched控制是否保留,之所以通過變量來決定是因為嵌套路由中父路由可能無法匹配,但是子路由能匹配所以父路由規(guī)則也需要子路參與是否保留。比如:

// 路由規(guī)則
const routers = new Router({
  base : "/test",
  // 定義默認路由比如登錄、404、401等
  routes : [{
    path : "/",
    ...
    children : [{
      path : "login",
      ...
    },{
      path : "about",
      ...
    },{
      path : "order",
      ...
      children : [{
        path : "id"
      }]
    }]
  }]
})

// 用戶權(quán)限
["/order/id"]; // 在匹配的過程中 "/" 不等于 "/order/id" 、"/" 不等于 "/order" 但是子路由 "/order/id" == "/order/id" 所以不但要保留 path : "/",還得保留 path : "order" 嵌套層。

2.5、動態(tài)注冊

// ...省略部分重復代碼

const openRouters = [];
const authRouters = [{
  path : "order/list",
  // ...
  meta : {
    // 是否身份驗證(至于默認定義false還是true由開發(fā)者自定義)
    isAuth : true
  }
}];

/* 動態(tài)注冊路由 */
async function AddRoutes() {
  // 獲取用戶路由權(quán)限
  let res = await POST(API.AUTH_RESOURCE_LISTSIDEMENU);
  try {
    const { code, data } = res || {};
    if (code === '000') {
      let newAuthRoutes = createAuthRouters(getAuthRouters(data))(authRouters, routes.options.base);
      // 注冊路由
      routes.addRoutes([].concat(newAuthRoutes, openRouters));
      // 設置已注冊
      Store.commit('UPDATE_IS_ADD_ROUTERS', true);
      // 保存菜單信息
      Store.commit('UPDATE_MENU_INFO', data);
    }
  } catch (error) {
    console.error('>>> AddRoutes() - error:', error);
  }
}

routes.beforeEach((to, from, next) => {
  const { meta, matched, path } = to;
  let isMatched = matched && matched.length > 0; // 是否匹配路由
  let isLogin = Cookie.get("token") || null;
  let { isAuth } = (meta || {});
  if(isMatched){
    // 匹配到路由
    if(isAuth){
      // 需要登錄訪問
      if(isLogin){
        // 已登錄訪問
        next(); // 調(diào)用鉤子函數(shù)
      }else{
        // 未登錄訪問
        next("/login"); // 跳轉(zhuǎn)登錄
      }
    }else{
      // 不需要登錄訪問
      next(); // 調(diào)用鉤子函數(shù)
    }
  }else{
    // 未匹配到路由
    if(isLogin){
      // 已登錄訪問
      AddRoutes();
      next();
    }else{
      // 未登錄訪問
      next("/login"); // 跳轉(zhuǎn)登錄
    }
  }
})

2.6、歸類整理

/* 路由前置 */
let { origin } = window.location || {};
routes.beforeEach((to, from, next) => {
  const { meta, matched, path } = to;
  let isMatched = matched && matched.length > 0; // 是否匹配
  let isAuth = (meta || {}).isAuth; // 是否授權(quán)訪問
  let { isAddRoutes } = Store.state; // 注冊路由
  let isLogin = Cookie.get('token') || null; // 是否登錄
  if ((isMatched && !isAuth) || (isMatched && isAuth && isLogin)) {
    // next()
    // 1.匹配路由 && 未登錄訪問
    // 2.匹配路由 && 登錄訪問 && 登錄
    next();
  } else if ((isMatched && isAuth && !isLogin) || (!isMatched && !isLogin)) {
    // 登錄
    // 1.匹配路由 && 登錄訪問 && 未登錄
    // 2.未匹配路由 && 未登錄
    next(`/login?r=${origin}/e-lottery${path}`);
  } else if (!isMatched && isLogin && isAddRoutes) {
    // 404
    // 1.未匹配路由 && 登錄 && 動態(tài)注冊路由
    next('/404');
  } else if (!isMatched && isLogin && !isAddRoutes) {
    // 注冊路由
    // 1.未匹配路由 && 登錄 && 未動態(tài)注冊路由
    AddRoutes();
    next();
  }
});

嗯! 這下看起來舒服多了。

3、完整實現(xiàn)代碼

// src/utils/index.js
const { pathToRegexp } = require('path-to-regexp');

export function getAuthRouters(authMenu) {
  let authRouters = [];
  (authMenu || []).forEach((item) => {
    const { resourceUrl, childMenu } = item;
    resourceUrl && authRouters.push(resourceUrl);
    if (childMenu && childMenu.length > 0) {
      // 合并子級菜單
      authRouters = [...authRouters, ...getAuthRouters(childMenu)];
    }
  });
  return authRouters;
}
/**
 *
 * @param { Array } authRouters
 */
export function createAuthRouters(authRouters) {
  const isAuthUrl = (url) => {
    return (authRouters || []).some((cUrl) => {
      return pathToRegexp(url).toString() === pathToRegexp(cUrl).toString();
    });
  };
  return function createRouters(routers, upperPath) {
    let nRouters = [];
    (routers || []).forEach((item) => {
      const { children, path, name } = item;
      let isMatched = false,
        nItem = { ...item },
        fullPath = `${upperPath || ''}/${path}`.replace(/\/{2,}/, '/'),
        nChildren = null;
      children && (nChildren = createRouters(children, fullPath));
      // 1.當前路由匹配
      if (isAuthUrl(fullPath)) {
        isMatched = true;
      }
      // 2.存在子路由匹配
      if (nChildren && nChildren.length > 0) {
        nItem.children = nChildren;
        isMatched = true;
      }
      // 特殊處理
      if(name === "home"){
        isMatched = true;
      }
      // nItem
      isMatched && nRouters.push(nItem);
    });
    return nRouters;
  };
}

 
// src/router.js

import Vue from 'vue';
import Store from '@/store';
import Router from 'vue-router';
import Cookie from 'js-cookie';

const openRouters = [];
const authRouters = [{
  path : "order/list",
  // ...
  meta : {
    // 是否身份驗證(至于默認定義false還是true由開發(fā)者自定義)
    isAuth : true
  }
}];

/* 動態(tài)注冊路由 */
async function AddRoutes() {
  // 獲取用戶路由權(quán)限
  let res = await POST(API.AUTH_RESOURCE_LISTSIDEMENU);
  try {
    const { code, data } = res || {};
    if (code === '000') {
      let newAuthRoutes = createAuthRouters(getAuthRouters(data))(authRouters, routes.options.base);
      // 注冊路由
      routes.addRoutes([].concat(newAuthRoutes, openRouters));
      // 設置已注冊
      Store.commit('UPDATE_IS_ADD_ROUTERS', true);
      // 保存菜單信息
      Store.commit('UPDATE_MENU_INFO', data);
    }
  } catch (error) {
    console.error('>>> AddRoutes() - error:', error);
  }
}


/* 路由前置 */
let { origin } = window.location || {};
routes.beforeEach((to, from, next) => {
  const { meta, matched, path } = to;
  let isMatched = matched && matched.length > 0; // 是否匹配
  let isAuth = (meta || {}).isAuth; // 是否授權(quán)訪問
  let { isAddRoutes } = Store.state; // 注冊路由
  let isLogin = Cookie.get('token') || null; // 是否登錄
  if ((isMatched && !isAuth) || (isMatched && isAuth && isLogin)) {
    // next()
    // 1.匹配路由 && 未登錄訪問
    // 2.匹配路由 && 登錄訪問 && 登錄
    next();
  } else if ((isMatched && isAuth && !isLogin) || (!isMatched && !isLogin)) {
    // 登錄
    // 1.匹配路由 && 登錄訪問 && 未登錄
    // 2.未匹配路由 && 未登錄
    next(`/login?r=${origin}/e-lottery${path}`);
  } else if (!isMatched && isLogin && isAddRoutes) {
    // 404
    // 1.未匹配路由 && 登錄 && 動態(tài)注冊路由
    next('/404');
  } else if (!isMatched && isLogin && !isAddRoutes) {
    // 注冊路由
    // 1.未匹配路由 && 登錄 && 未動態(tài)注冊路由
    AddRoutes();
    next();
  }
});

雖然前端能夠通過vue-router實現(xiàn)對路由權(quán)限的控制,但是實際是偽權(quán)限控制,無法達到完全控制;強烈建議對于需要控制路由權(quán)限的系統(tǒng)采用后端控制。

到此這篇關于vue-router 控制路由權(quán)限的實現(xiàn)的文章就介紹到這了,更多相關vue-router 控制路由權(quán)限內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家! 

相關文章

  • vue實現(xiàn)購物車功能(親測可用)

    vue實現(xiàn)購物車功能(親測可用)

    這篇文章主要為大家詳細介紹了vue實現(xiàn)購物車功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • 淺談實現(xiàn)在線預覽PDF的幾種解決辦法

    淺談實現(xiàn)在線預覽PDF的幾種解決辦法

    這篇文章主要介紹了淺談實現(xiàn)在線預覽PDF的幾種解決辦法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • Vue編譯器解析compile源碼解析

    Vue編譯器解析compile源碼解析

    這篇文章主要為大家介紹了Vue編譯器解析compile源碼解析示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-07-07
  • Vue3新狀態(tài)管理工具實例詳解

    Vue3新狀態(tài)管理工具實例詳解

    Vue3公布曾經(jīng)有一段時間了,它采納了新的響應式零碎,而且構(gòu)建了一套全新的 Composition API,下面這篇文章主要給大家介紹了關于Vue3新狀態(tài)管理工具的相關資料,需要的朋友可以參考下
    2022-03-03
  • Vue項目保存代碼之后頁面自動更新問題

    Vue項目保存代碼之后頁面自動更新問題

    這篇文章主要介紹了Vue項目保存代碼之后頁面自動更新問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • vue3-print-nb實現(xiàn)頁面打印(含分頁打印)示例代碼

    vue3-print-nb實現(xiàn)頁面打印(含分頁打印)示例代碼

    大多數(shù)后臺系統(tǒng)中都存在打印的需求,在有打印需求時,對前端來說當然是直接打印頁面更容易,下面這篇文章主要給大家介紹了關于vue3-print-nb實現(xiàn)頁面打印(含分頁打印)的相關資料,需要的朋友可以參考下
    2024-01-01
  • vue-cli-service serve報錯error:0308010C:digital envelope routines::unsupported

    vue-cli-service serve報錯error:0308010C:digital enve

    這篇文章主要介紹了vue-cli-service serve報錯error:0308010C:digital envelope routines::unsupported的解決分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • vue 2.0項目中如何引入element-ui詳解

    vue 2.0項目中如何引入element-ui詳解

    element-ui是一個比較完善的UI庫,但是使用它需要有一點vue的基礎,下面這篇文章主要給大家介紹了關于在vue 2.0項目中如何引入element-ui的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-09-09
  • Vue3計算屬性和異步計算屬性方式

    Vue3計算屬性和異步計算屬性方式

    這篇文章主要介紹了Vue3計算屬性和異步計算屬性方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • vue 集成jTopo 處理方法

    vue 集成jTopo 處理方法

    這篇文章主要介紹了vue 集成jTopo 處理方法,非常不錯,具有一定的參考借鑒價值 ,需要的朋友可以參考下
    2019-08-08

最新評論

白水县| 浮山县| 阳朔县| 龙井市| 夏津县| 镇江市| 富宁县| 龙井市| 凌海市| 三台县| 庄浪县| 阿拉善右旗| 阿城市| 石首市| 仁布县| 芦山县| 衡山县| 建平县| 通州区| 若羌县| 安新县| 万山特区| 土默特右旗| 斗六市| 花莲县| 信宜市| 西藏| 墨脱县| 威信县| 英德市| 房山区| 仲巴县| 定远县| 区。| 天等县| 北川| 浦北县| 邵阳县| 大英县| 苍梧县| 于田县|