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

React實現(xiàn)路由鑒權的實例詳解

 更新時間:2024年07月01日 09:12:03   作者:sole  
React應用中的路由鑒權是確保用戶僅能訪問其授權頁面的方式,用于已登錄或具有訪問特定頁面所需的權限,這篇文章就來記錄下React實現(xiàn)路由鑒權的流程,需要的朋友可以參考下

前言

React應用中的路由鑒權是確保用戶僅能訪問其授權頁面的方式,用于已登錄或具有訪問特定頁面所需的權限,由于React沒有實現(xiàn)類似于Vue的路由守衛(wèi)功能,所以只能由開發(fā)者自行實現(xiàn)。前端中的路由鑒權可以區(qū)分以下顆粒度:菜單權限控制、組件權限控制、路由權限控制。在后臺管理系統(tǒng)中三者是必不可少的。這篇文章就來記錄下React實現(xiàn)路由鑒權的流程。

確定權限樹

開始之前我們需要對權限樹的數(shù)據結構進行確定,一般來說我們需要拿到后端傳回的權限樹進行存儲,在React中通常將權限樹存儲在Redux中,并且我們前端也需要自行維護一顆屬于前端的權限樹,這里需要兩個權限樹進行對比從而實現(xiàn)路由鑒權。權限樹結構如下:

export type LocalMenuType = {
  key: string;
  level: number; // 層級
  menucode: string; // 權限碼 用于查詢是否有此權限
  label?: React.ReactNode; // 菜單名稱
  path?: string | string[]; // 路由
  parentmenuid?: string; // 父級菜單id
  children?: LocalMenuType[]; // 子菜單
};

權限樹處理

在確定權限樹后,我們需要對權限樹結構進行打平。為此我們可以寫個utils來處理權限樹。我們使用遞歸來進行打平權限樹:

export const getFlattenList = (
  authList: LocalMenuType[], // 需要打平的權限樹
  flattenAuthList: LocalMenuType[], // 存儲打平后的權限樹
  level?: number // 打平層級
) => {
  authList.forEach((item) => {
    // 如果查找層級超出則返回
    if (level && item.level > level) return;
    flattenAuthList.push(Object.assign({}, item, { children: [] }));
    if (item.children && item.children.length > 0) {
      getFlattenList(item.children, flattenAuthList, level);
    }
  });
};

通過以上代碼我們將一個樹打平,打平后結構如下:

菜單權限控制

在后臺系統(tǒng)中,每個角色有不同的菜單權限,我們需要根據后端返回的角色數(shù)據進行菜單的顯示與隱藏,為了方便起見這里直接使用mock數(shù)據并將數(shù)據存儲在localStorage中:

export enum RoleType {
  MANAGER,
  ONLY_ORDER,
  ONLY_VIEW_LIST,
}

export const setCurrentRole = (type: RoleType) => {
  switch (type) {
    case RoleType.MANAGER: {
      window.localStorage.setItem("menu", JSON.stringify(menus));
      break;
    }
    case RoleType.ONLY_ORDER: {
      window.localStorage.setItem("menu", JSON.stringify(onlyOrder));
      break;
    }
    case RoleType.ONLY_VIEW_LIST: {
      window.localStorage.setItem("menu", JSON.stringify(onlyViewList));
      break;
    }
  }
};

export const getCurrentRole = () =>
  JSON.parse(window.localStorage.getItem("menu")) as LocalMenuType[];

通過localStorage得到的角色信息進行顯示菜單,這里我們只顯示1,2級菜單,所以需要對權限菜單進行處理,只保留1,2的菜單數(shù)據,也是需要用到遞歸來處理:

// 處理權限菜單
const handleAuthMenu = (
  menuList: LocalMenuType[],// 全部菜單權限樹
  authCodes: string[], // 角色所有權限
  authMenuList: LocalMenuType[], // 角色最終擁有菜單列表
  level?: number // 處理層級
) => {
  menuList.forEach((menu) => {
    // 如果level 存在,則只處理小于level的情況
    if (level && menu.level > level) return;
    // 如果有權限,則繼續(xù)遞歸遍歷菜單
    if (authCodes.includes(menu.menucode)) {
      let newAuthMenu: LocalMenuType = { ...menu, children: undefined };

      let newAuthMenuChildren: LocalMenuType[] = [];
      if (menu.children && menu.children.length > 0) {
        handleAuthMenu(menu.children, authCodes, newAuthMenuChildren, level);
      }
      // 添加子菜單
      if (newAuthMenuChildren.length > 0) {
        newAuthMenu.children = newAuthMenuChildren;
      }
      authMenuList.push(newAuthMenu);
    }
  });
};
// 獲取角色權限菜單
export const getAuthMenu = (flattenAuth: LocalMenuType[], level?: number) => {
  // 獲取權限菜單的menucode
  const authCodes: string[] = flattenAuth.map((auth) => auth.menucode);
  let authMenu: LocalMenuType[] = [];

  handleAuthMenu(menus, authCodes, authMenu, level);

  return authMenu;
};

在獲取完角色1,2級菜單后,我們需要對左側菜單欄進行初始化,默認為菜單列表中第一個path。獲取二級菜單的首位菜單路由之后通過useNavigate進行跳轉。獲取菜單路由通過getRoutePath進行獲取,由于一個頁面可能包含多個路由,所以需要對path信息進行判斷:

// anthRoles.ts
// 獲取菜單路由
export const getRoutePath = (localMenu: LocalMenuType) => {
  return localMenu.path
    ? typeof localMenu.path === "object"
      ? localMenu.path[0]
      : localMenu.path
    : null;
};
// home.tsx
//2級菜單list
const secondAuthMenuList = useMemo(() => {
    return flattenList.filter((res) => res.level === 2);
  }, [flattenList]);
// 初始化菜單
  useEffect(() => {
    const initMenuItem = secondAuthMenuList[0];
    if (initMenuItem) {
      const initRoute =
        initMenuItem.level > 2 ? pathname : getRoutePath(initMenuItem)!;
      navigate(initRoute, { replace: true });
    }
  }, [secondAuthMenuList, flattenList]);

那如何根據點擊的菜單進行跳轉呢?也是需要獲取對應key值的二級菜單path進行跳轉:

const findSecondMenuByKey = (key: string) =>
    secondAuthMenuList.find((item) => item.key === key);
    
    // 點擊菜單進行跳轉
 const handleMenuChange = ({ key }: { key: string }) => {
    setMenuSelectKeys([key]);
    let chooseItem = findSecondMenuByKey(key);
    if (chooseItem?.path) navigate(getRoutePath(chooseItem) || "");
  };

最終實現(xiàn)效果如下:

組件權限控制

組件權限控制相對簡單,需要通過menucode也就是權限碼進行組件的顯示與隱藏,這里以按鈕組件為例子,我們需要一個高階組件AuthBuutonHOC作為按鈕組件的父組件進行顯示,同時,我們通過hasAuth函數(shù)判斷是否有當前指定權限:

// 是否有當前權限
export const hasAuth = (meunCode: string) => {
  // 當前打平的角色權限樹
  let flattenAuthList: LocalMenuType[] = getCurrentFlattenRole();
  return !!flattenAuthList.find((auth) => auth.menucode === meunCode);
};
AuthBuutonHOC.tsx

const AuthButton: React.FC<Props> = ({ menuCode, children }) => {
  // 沒有當前權限則不顯示
  if (!hasAuth(menuCode)) return null;

  return <>{children}</>;
};

export default React.memo(AuthButton);

使用AuthButton包裹按鈕,就能實現(xiàn)組件級別的權限控制:

  • 有當前權限:

  • 無當前權限:

路由權限控制

路由權限控制需要對用戶輸入的路徑名進行校驗,我們通過useLocation獲取到當前用戶輸入的pathname;并路徑匹配matchPath判斷當前路徑與權限菜單路徑是否對應,若對應上則表示當前角色擁有權限,若對應不上則跳轉到404頁面。

通過以上邏輯,我們先創(chuàng)建一個hasAuthByRoutePath函數(shù)來判斷是否有當前的路由權限:

// 是否有當前的路由權限
export const hasAuthByRoutePath = (path: string) => {
  let flattenAuthList: LocalMenuType[] = getCurrentFlattenRole();
  return !!flattenAuthList.find((auth) =>
    routePathMatch(path, auth.path || "")
  );
};

// 判斷路由是否一致
export const routePathMatch = (path: string, menuPath: string | string[]) => {
  if (typeof menuPath === "object") {
    return menuPath.some((item) => matchPath(item, path));
  }

  return !!matchPath(menuPath, path);
};

在home頁面監(jiān)聽用戶輸入的路徑名進行判斷,有則跳轉到當前菜單:

useEffect(() => {
    // 獲取當前匹配到的菜單
    const matchMenuItem = flattenList.find((item) =>
      routePathMatch(pathname, item.path || "")
    );
    if (matchMenuItem) {
      // 如果當前菜單level為3級或者更小則設置其父id,否則設置其id
      matchMenuItem.level > 2
        ? setMenuSelectKeys([matchMenuItem.parentmenuid!])
        : setMenuSelectKeys([matchMenuItem.key]);
      // 如果當前菜單level為3級或者更小則通過父id找到2級菜單
      const newSecondMenu =
        matchMenuItem.level > 2
          ? findSecondMenuByKey(matchMenuItem.parentmenuid!)
          : matchMenuItem;
      // 有對應的二級菜單則定位到當前側邊菜單欄位置
      if (newSecondMenu) {
        setMenuOpenKeys((preOpenKeys) => {
          const openKeysSet = new Set(preOpenKeys || []);
          openKeysSet.add(newSecondMenu.parentmenuid!);
          return Array.from(openKeysSet);
        });
      } else {
        setMenuSelectKeys([]);
      }
    }
  }, [secondAuthMenuList, flattenList, pathname]);

最后,如果沒有當前路徑權限則跳轉到404頁面,為此我們需要一個authLayout高階組件來包裹HomePage來實現(xiàn)跳轉:

// 白名單
const routerWhiteList = ["/home"];

const AuthLayout = ({ children }: { children: JSX.Element }) => {
  const { pathname } = useLocation();

  // 判斷當前路由是否在白名單內或者有當前權限路由
  const hasAuthRoute = useMemo(() => {
    return (
      routePathMatch(pathname, routerWhiteList) || hasAuthByRoutePath(pathname)
    );
  }, [pathname]);
   // 沒有權限則跳轉至404頁面
  if (!hasAuthRoute) return <Navigate to="/404" replace />;

  return children;
};

export default AuthLayout;

將以上組件包裹在HomePage外層即可實現(xiàn)路由權限控制:

{
    path: "/home",
    element: (
      <AuthLayout>
        <HomePage />
      </AuthLayout>
    ),
}

具體實現(xiàn)效果如下:

總結

到此這篇關于React實現(xiàn)路由鑒權的實例詳解的文章就介紹到這了,更多相關React路由鑒權內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • React?組件之props屬性的使用

    React?組件之props屬性的使用

    在React中,props是組件之間傳遞數(shù)據的一種方式,它是?React?組件的基礎,用于將數(shù)據從父組件傳遞到子組件,本文就來詳細的介紹一下,感興趣的可以了解一下
    2025-09-09
  • React Native中實現(xiàn)動態(tài)導入的示例代碼

    React Native中實現(xiàn)動態(tài)導入的示例代碼

    隨著業(yè)務的發(fā)展,每一個 React Native 應用的代碼數(shù)量都在不斷增加。作為一個前端想到的方案自然就是動態(tài)導入(Dynamic import)了,本文介紹了React Native中實現(xiàn)動態(tài)導入的示例代碼,需要的可以參考一下
    2022-06-06
  • 詳細分析React 表單與事件

    詳細分析React 表單與事件

    這篇文章主要介紹了React 表單與事件的相關資料,文中示例代碼非常詳細,幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-07-07
  • 探究react-native 源碼的圖片緩存問題

    探究react-native 源碼的圖片緩存問題

    本篇文章主要介紹了探究react-native 源碼的圖片緩存問題,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-08-08
  • react項目中@路徑簡單配置指南

    react項目中@路徑簡單配置指南

    這篇文章主要給大家介紹了關于react項目中@路徑簡單配置的相關資料,文中還介紹了React配置@路徑別名的方法,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-09-09
  • 基于react框架使用的一些細節(jié)要點的思考

    基于react框架使用的一些細節(jié)要點的思考

    下面小編就為大家?guī)硪黄趓eact框架使用的一些細節(jié)要點的思考。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • 詳解使用React.memo()來優(yōu)化函數(shù)組件的性能

    詳解使用React.memo()來優(yōu)化函數(shù)組件的性能

    本文講述了開發(fā)React應用時如何使用shouldComponentUpdate生命周期函數(shù)以及PureComponent去避免類組件進行無用的重渲染,以及如何使用最新的React.memo API去優(yōu)化函數(shù)組件的性能
    2019-03-03
  • 歸納總結Remix?表單常用方法及示例詳解

    歸納總結Remix?表單常用方法及示例詳解

    這篇文章主要為大家歸納總結了Remix?表單常用方法及示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • React實現(xiàn)登錄界面的項目實踐

    React實現(xiàn)登錄界面的項目實踐

    本文主要介紹了用React創(chuàng)建登錄表單,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-09-09
  • 使用React代碼動態(tài)生成柵格布局的方法

    使用React代碼動態(tài)生成柵格布局的方法

    這篇文章主要介紹了使用React簡短代碼動態(tài)生成柵格布局的方法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-05-05

最新評論

长宁区| 天门市| 平顺县| 广元市| 织金县| 安阳市| 天峻县| 南宁市| 来凤县| 辉县市| 怀集县| 修文县| 泉州市| 彭山县| 浑源县| 娱乐| 万年县| 尼木县| 读书| 久治县| 赣榆县| 筠连县| 临邑县| 建始县| 永修县| 德兴市| 方城县| 青田县| 富锦市| 本溪| 漳浦县| 饶平县| 茌平县| 永年县| 永善县| 郧西县| 江山市| 桦甸市| 易门县| 邳州市| 宜兰市|