欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React-Router(V6)的權(quán)限控制實(shí)現(xiàn)示例

 更新時(shí)間:2023年05月05日 15:22:28   作者:SaebaRyo  
本文主要介紹了React-Router(V6)的權(quán)限控制實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

在一個(gè)后臺(tái)管理系統(tǒng)中,安全是很重要的。不光后端需要做權(quán)限校驗(yàn),前端也需要做權(quán)限控制。 我們可以大致將權(quán)限分為3種: 接口權(quán)限、頁(yè)面權(quán)限、按鈕權(quán)限。

在這當(dāng)中,前端主要關(guān)注點(diǎn)則是頁(yè)面權(quán)限,按鈕權(quán)限,而前端做這些的主要目的則是:

  • 禁止用戶訪問一些無(wú)權(quán)限訪問的頁(yè)面
  • 過(guò)濾不必要的請(qǐng)求,減少服務(wù)器壓力

下面主要是思路的整理,以及一些核心實(shí)現(xiàn)

接口權(quán)限

接口權(quán)限一般是用戶登錄后,后端根據(jù)賬號(hào)密碼來(lái)認(rèn)證授權(quán),并頒發(fā)token或者session等來(lái)保存用戶登錄狀態(tài)。

后續(xù)客戶端請(qǐng)求一般是在header中攜帶token,后端通過(guò)對(duì)token進(jìn)行鑒權(quán)是否合法來(lái)控制是否可以訪問接口。

一般后臺(tái)會(huì)通過(guò)用戶的角色等來(lái)做對(duì)應(yīng)的接口權(quán)限控制。

而需要我們前端做的是在請(qǐng)求中攜帶好登錄后回傳的token,我們以axios為例

const instance = axios.create(config);
instance.interceptors.request.use(
  (request: any) => {
    request.headers["access_token"] = localStorage.getItem("access_token");
    return request;
  },
  (err) => {
    Promise.reject(err.response);
  }
);
instance.interceptors.response.use(
  (response) => {
    if (response.status !== 200) return Promise.reject(response.data);
    if (response.data.code === 401) {
      //token過(guò)期或者錯(cuò)誤
      window.location.replace("/login");
    }
    return response.data.data;
  },
  (err) => {
    Promise.reject(err.response);
  }
);

頁(yè)面權(quán)限

首先,我們先完成路由配置

src/routes/routes.tsx

export type RoutesType = {
  path: string;
  element: ReactElement;
  children?: RoutesType[];
};
const routers: RoutesType[] = [
  {
    path: "/login",
    element: <Login />,
  },
  {
    path: "/",
    element: <Home />,
  },
  {
    path: "/foo",
    element: <Foo />,
    children: [
      {
        path: "/foo/auth-button",
        element: <MyAuthButtonPage />,
      },
    ],
  },
  {
    path: "/protected",
    element: <Protected />,
  },
  {
    path: "/unauthorized",
    element: <UnauthorizedPage />,
  },
  // 配置404,需要放在最后
  {
    path: "/*",
    element: <NotFound />,
  },
];

然后是基于路由配置來(lái)生成對(duì)應(yīng)的路由組件

src/routes/root.tsx

const Root = () => {
  // 創(chuàng)建一個(gè)有子節(jié)點(diǎn)的Route
  const CreateHasChildrenRoute = (route: RoutesType) => {
    return (
      <Route path={route.path} key={route.path}>
        <Route
          index
          element={
            <AuthRoute key={route.path} path={route.path}>
              {route.element}
            </AuthRoute>
          }
        />
        {route?.children && RouteAuthFun(route.children)}
      </Route>
    );
  };
  // 創(chuàng)建一個(gè)沒有子節(jié)點(diǎn)的Route
  const CreateNoChildrenRoute = (route: RoutesType) => {
    return (
      <Route
        key={route.path}
        path={route.path}
        element={
          <AuthRoute path={route.path} key={route.path}>
            {route.element}
          </AuthRoute>
        }
      />
    );
  };
  // 處理我們的routers
  const RouteAuthFun = (routeList: any) => {
    return routeList.map((route: RoutesType) => {
      let element: ReactElement | null = null;
      if (route.children && !!route.children.length) {
        element = CreateHasChildrenRoute(route);
      } else {
        element = CreateNoChildrenRoute(route);
      }
      return element;
    });
  };
  return (
    <BrowserRouter>
      <Routes>{RouteAuthFun(routers)}</Routes>
    </BrowserRouter>
  );
};

最后是只需要在入口中寫入Root組件即可

ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
  <Provider store={store}>
    <Root />
  </Provider>
);

上面只是完成了基本的配置,下面才是權(quán)限相關(guān)

路由權(quán)限主要分為兩個(gè)方向:

1. 菜單權(quán)限

一般來(lái)說(shuō),后臺(tái)通過(guò)維護(hù)user、rolemenu、user_role、menu_role這幾張表來(lái)做相應(yīng)的權(quán)限設(shè)計(jì)。

所以,在登錄接口中,一般后臺(tái)會(huì)返回用戶對(duì)應(yīng)的角色、菜單等信息。我們通過(guò)redux-toolkit保存登錄數(shù)據(jù)。大致信息如下(未真正請(qǐng)求接口,只寫了初始數(shù)據(jù)):

src/pages/login/Login.slice.ts

interface LoginState {
  username: string;
  role: string;
  menuLists: any[];
}
// Define the initial state using that type
const initialState: LoginState = {
  username: "ryo",
  role: "admin",
  menuLists: [
    {
      id: "1",
      name: "首頁(yè)",
      icon: "icon-home",
      url: "/",
      parent_id: "0",
    },
    {
      id: "2",
      name: "foo",
      icon: "icon-foo",
      url: "/foo",
      parent_id: "0",
    },
    {
      id: "2-1",
      name: "auth-button",
      icon: "icon-auth-button",
      url: "/foo/auth-button",
      parent_id: "2",
    },
  ],
};

這里的role表示當(dāng)前用戶的角色,menuLists為用戶可訪問的菜單

然后在首頁(yè)中生成菜單列表

const getMenuItem = (menus: any): any => {
  return menus.map((menu: any) => {
    if (menu.children) {
      return (
        <div key={menu.url}>
          <Link to={menu.url}>{menu.name}</Link>
          {getMenuItem(menu.children)}
        </div>
      );
    }
    return (
      <div key={menu.url}>
        <Link to={menu.url}>{menu.name}</Link>
      </div>
    );
  });
};
function genMenu(array: any, parentId = "0") {
  const result = [];
  for (const item of array) {
    if (item.parent_id === parentId) {
      const menu = { ...item };
      menu.children = genMenu(array, menu.id);
      result.push(menu);
    }
  }
  return result;
}
function Home() {
  const menuLists = useAppSelector((state) => state.login.menuLists);
  const menuTree = genMenu(menuLists);
  return (
    <div>
      <h1>home page</h1>
      {getMenuItem(menuTree)}
    </div>
  );
}
export default Home;

但是,只根據(jù)權(quán)限列表來(lái)動(dòng)態(tài)生成菜單并不能完全實(shí)現(xiàn)權(quán)限相關(guān)的目的。用戶還可以通過(guò)在地址欄輸入url的方式來(lái)訪問沒有在菜單中顯示的頁(yè)面。

2. 路由權(quán)限

我們可以通過(guò)實(shí)現(xiàn)一個(gè)AuthRoute來(lái)解決上述的問題。

通過(guò)AuthRoute來(lái)攔截頁(yè)面的訪問操作。

src/routes/AuthRoute.tsx

// 無(wú)需權(quán)限認(rèn)證的白名單
// 一般是前端的一些報(bào)錯(cuò)頁(yè)
const DONT_NEED_AUTHORIZED_PAGE = ["/unauthorized", "/*"];
const AuthRoute = ({ children, path }: any) => {
  // 該flag用于控制 受保護(hù)頁(yè)面的渲染時(shí)機(jī),需要等待useEffect中所有的權(quán)限驗(yàn)證條件完成后才表示可以渲染
  const [canRender, setRenderFlag] = useState(false);
  const navigate = useNavigate();
  const menuLists = useAppSelector((state) => state.login.menuLists);
  const menuUrls = menuLists.map((menu) => menu.url);
  const token = localStorage.getItem("access_token") || "";
  // 在白名單中的無(wú)需驗(yàn)證,直接跳轉(zhuǎn)
  if (DONT_NEED_AUTHORIZED_PAGE.includes(path)) {
    return children;
  }
  useEffect(() => {
    // 用戶未登錄
    if (token === "") {
      message.error("token 過(guò)期,請(qǐng)重新登錄!");
      navigate("/login");
    }
    // 已登錄
    if (token) {
      // 已登錄需要通過(guò)logout來(lái)控制退出登錄或者是token過(guò)期返回登錄界面
      if (location.pathname == "/login") {
        navigate("/");
      }
      // 已登錄,根據(jù)后臺(tái)傳的權(quán)限列表做判斷
      if (!menuUrls.includes(location.pathname)) {
        navigate("/unauthorized", { replace: true });
      }
    }
    // 當(dāng)上面的權(quán)限控制通過(guò)后,再渲染受保護(hù)的頁(yè)面
    setRenderFlag(true);
  }, [token, location.pathname]);
  if (!canRender) return null;
  return children;
};
export default AuthRoute;

然后,在我們生成Route的時(shí)候在element屬性中使用AuthRoute,這一步,我們已經(jīng)在上面src/routes/root.tsx這個(gè)文件中寫進(jìn)去了。

到這里,我們就通過(guò)實(shí)現(xiàn)AuthRoute來(lái)攔截頁(yè)面訪問,做權(quán)限相關(guān)處理。

然后我們可以運(yùn)行該倉(cāng)庫(kù) 代碼來(lái)看效果。

目前沒有實(shí)現(xiàn)登錄相關(guān)功能,所以需要手動(dòng)在localStorage中添加access_token來(lái)模擬登錄。

  • 如果沒有登錄(沒有access_token)或者登錄已過(guò)期,訪問任何路由都會(huì)被路由到/login。
  • 如果已經(jīng)登錄,但是再訪問登錄頁(yè)面,會(huì)被路由到/首頁(yè)
  • 如果已經(jīng)登錄,但是訪問了一個(gè)你無(wú)訪問的頁(yè)面,如/protected,則會(huì)被路由到/unauthorized頁(yè)面

按鈕權(quán)限

按鈕級(jí)別的權(quán)限,根據(jù)當(dāng)前用戶角色的不同,可以看到的按鈕和操作不同。這里我只簡(jiǎn)單實(shí)現(xiàn)了一個(gè)AuthButton

src/coponents/auth-button/index.tsx

import { Button } from "antd";
import type { ButtonProps } from "antd";
import React from "react";
import { useAppSelector } from "../../hooks/typedHooks";
interface AuthButtonProps extends ButtonProps {
  roles: string[];
}
const AuthButton: React.FC<AuthButtonProps> = ({ roles, children }) => {
  const role = useAppSelector((state) => state.login.role);
  if (roles.includes(role)) {
    return <Button>{children}</Button>;
  }
  return null;
};
export default AuthButton;

使用方法如下,新增了一個(gè)roles屬性,表示哪些角色可以看見該按鈕

src/pages/foo/auth-button.tsx

const ButtonPermission: React.FC = () => {
  const role = useAppSelector((state) => state.login.role);
  return (
    <div>
      <h1>Button Permission</h1>
      <AuthButton roles={["admin", "user"]}>添加</AuthButton>
      <AuthButton roles={["admin"]}>編輯</AuthButton>
      <AuthButton roles={["admin"]}>刪除</AuthButton>
    </div>
  );
};
export default ButtonPermission;

我們可以手動(dòng)的修改Login.slice.ts中的role來(lái)查看不同的情況。

這種實(shí)現(xiàn)方式比較簡(jiǎn)單,大伙可以根據(jù)自己的具體場(chǎng)景選擇更好的方案

參考

到此這篇關(guān)于React-Router(V6)的權(quán)限控制實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)React-Router權(quán)限控制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • React-RouterV6+AntdV4實(shí)現(xiàn)Menu菜單路由跳轉(zhuǎn)的方法

    React-RouterV6+AntdV4實(shí)現(xiàn)Menu菜單路由跳轉(zhuǎn)的方法

    這篇文章主要介紹了React-RouterV6+AntdV4實(shí)現(xiàn)Menu菜單路由跳轉(zhuǎn),主要有兩種跳轉(zhuǎn)方式一種是編程式跳轉(zhuǎn)另一種是NavLink鏈接式跳轉(zhuǎn),每種方式通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • React實(shí)現(xiàn)動(dòng)效彈窗組件

    React實(shí)現(xiàn)動(dòng)效彈窗組件

    最近在使用react開發(fā)項(xiàng)目,遇到這樣一個(gè)需求實(shí)現(xiàn)一個(gè)帶有動(dòng)效的 React 彈窗組件,如果不考慮動(dòng)效,很容易實(shí)現(xiàn),接下來(lái)小編通過(guò)本文給大家介紹React實(shí)現(xiàn)動(dòng)效彈窗組件的實(shí)現(xiàn)代碼,一起看看吧
    2021-06-06
  • 再次談?wù)揜eact.js實(shí)現(xiàn)原生js拖拽效果引起的一系列問題

    再次談?wù)揜eact.js實(shí)現(xiàn)原生js拖拽效果引起的一系列問題

    React 起源于 Facebook 的內(nèi)部項(xiàng)目,因?yàn)樵摴緦?duì)市場(chǎng)上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來(lái)架設(shè) Instagram 的網(wǎng)站.本文給大家介紹React.js實(shí)現(xiàn)原生js拖拽效果,需要的朋友一起學(xué)習(xí)吧
    2016-04-04
  • React使用UI(Ant?Design)框架的詳細(xì)過(guò)程

    React使用UI(Ant?Design)框架的詳細(xì)過(guò)程

    Ant?Design主要用于中后臺(tái)系統(tǒng)的使用,它提供了豐富的組件和工具,可以幫助開發(fā)人員快速構(gòu)建出美觀、易用的界面,同時(shí),Ant?Design還提供了詳細(xì)的文檔和示例,方便開發(fā)者學(xué)習(xí)和使用,這篇文章主要介紹了React使用UI(Ant?Design)框架,需要的朋友可以參考下
    2023-12-12
  • React?腳手架配置代理完整指南(最新推薦)

    React?腳手架配置代理完整指南(最新推薦)

    本文詳細(xì)介紹了React腳手架配置代理的多種方式,文章還討論了常見問題的解決方案,如跨域問題、WebSocket代理和錯(cuò)誤處理,并提供了生產(chǎn)環(huán)境配置建議和調(diào)試技巧,感興趣的朋友一起看看吧
    2024-12-12
  • React.cloneElement的使用詳解

    React.cloneElement的使用詳解

    這篇文章主要介紹了React.cloneElement的使用詳解,幫助大家更好的理解和學(xué)習(xí)使用React框架,感興趣的朋友可以了解下
    2021-04-04
  • React之關(guān)于Promise的用法

    React之關(guān)于Promise的用法

    這篇文章主要介紹了React之關(guān)于Promise的用法及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • React文件分段上傳實(shí)現(xiàn)方法詳解

    React文件分段上傳實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了React文件分段上傳實(shí)現(xiàn)方法,將文件切成多個(gè)小的文件;將切片并行上傳;所有切片上傳完成后,服務(wù)器端進(jìn)行切片合成;當(dāng)分片上傳失敗,可以在重新上傳時(shí)進(jìn)行判斷,只上傳上次失敗的部分實(shí)現(xiàn)斷點(diǎn)續(xù)傳;當(dāng)切片合成為完整的文件,通知客戶端上傳成功
    2022-11-11
  • react高階組件添加和刪除props

    react高階組件添加和刪除props

    這篇文章主要介紹了react高階組件添加和刪除props,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • 用React實(shí)現(xiàn)一個(gè)完整的TodoList的示例代碼

    用React實(shí)現(xiàn)一個(gè)完整的TodoList的示例代碼

    本篇文章主要介紹了用React實(shí)現(xiàn)一個(gè)完整的TodoList的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10

最新評(píng)論