需求

  1. 未登录状态下,某些页面不可访问,白名单中的页面可以。
  2. 未登录状态下,拦截通过修改url直接访问页面。
  3. 判断是否有权访问某些页面。
  4. 路由规则中每个页面都需要调用某个接口。

前提

使用的react-router-dom6 ,这里只是举例,具体细节根据项目调整。

路由表生成路由规则

import { Navigate, Outlet, RouteObject, useRoutes } from "react-router-dom"

import MyLayout from "@/layout/index"
// 无需Layout的组件
import Login from "@/views/Login/Login"
import NotFound from "@/views/NotFound/NotFound"
// 组件
import Dashboard from "@/views/Dashborad/Dashboard"
import Project from "@/views/Project/Project"
import Test1 from "@/views/Setting/Test1"
import Test2 from "@/views/Setting/Test2"
import Test3 from "@/views/Test/Test3"
import Test4 from "@/views/Test/Test4"

const router_items: RouteObject[] = [
  {
    path: "/",
    element: <MyLayout />,
    children: [
      {
        path: "",
        element: <Navigate to="/dashboard" />, // 重定向
      },
      {
        path: "dashboard",
        element: <Dashboard />,
      },
      {
        path: "project",
        element: <Project />,
      },
      {
        path: "setting",
        element: <Outlet />, // 占位符
        children: [
          { path: "test1", element: <Test1 /> },
          { path: "test2", element: <Test2 /> },
        ],
      },
      {
        path: "test",
        element: <Outlet />, // 占位符
        children: [
          { path: "test3", element: <Test3 /> },
          { path: "test4", element: <Test4 /> },
        ],
      },
    ],
  },
  // 不需要layout的页面写到外面
  {
    path: "login",
    element: <Login />,
  },
  { path: "*", element: <NotFound /> },
]

export default () => {
  // 根据路由表生成对应的路由规则
  const ElementRouter = useRoutes(router_items)
  return ElementRouter
}

上述路由规则会在<MyLayout />中声明的位置处展示

【React】路由鉴权-LMLPHP
APP中注册路由

// 路由
import GetRouter from "./router"

function App() {
  const RouterElement = GetRouter()
  return <>{RouterElement}</>
}

export default App

实现步骤

先创建高阶组件名为AuthRouter,并在main.tsx中引入并包裹APP。
高阶组件指接收一个组件并返回增强后的该组件。

AuthRouter

const AuthRouter = (props: { children: JSX.Element }) => {
  return props.children
}

export default AuthRouter
import ReactDOM from "react-dom/client"
import App from "./App.tsx"
import "./index.scss"
// router
import { BrowserRouter as Router } from "react-router-dom"
import AuthRouter from "@/HOC/AuthRouter.tsx" // 高阶组件


ReactDOM.createRoot(document.getElementById("root")!).render(
  <Router>
        <AuthRouter>
          <App />
        </AuthRouter>
  </Router>
)

token相关

一、token失效重定向到login

AuthRouter

import { Navigate, useLocation } from "react-router-dom"

const AuthRouter = (props: { children: JSX.Element }) => {
  const { pathname } = useLocation()

  let token = "sdnfowe623ognis"
  if (pathname === "/login") {
    return props.children
  }
  if (!token) {
    return <Navigate to="/login" replace />
  } else {
    return props.children
  }
}

export default AuthRouter

二、白名单无需判断token

AuthRouter

import { Navigate, useLocation } from "react-router-dom"

const AuthRouter = (props: { children: JSX.Element }) => {
  const { pathname } = useLocation()
  
  const whiteList = ["/login", "/test/test4"] // 声明白名单
  
  let token = "sdnfowe623ognis"
  // 白名单直接放行
  if (whiteList.includes(pathname)) {  
    return props.children
  }
  if (!token) {
    return <Navigate to="/login" replace />
  } else {
    return props.children
  }
}

export default AuthRouter

权限相关

基本同白名单逻辑

AuthRouter

import { useLocation } from "react-router-dom"

const AuthRouter = (props: { children: JSX.Element }) => {
  const { pathname } = useLocation()

  let auth_list = ["/login", "/dashboard"] // 权限只能访问这些路由
  if (!auth_list.includes(pathname)) {
    return (
      <div>
        <h2>暂无权限</h2>
      </div>
    )
  }
  return props.children
}

export default AuthRouter

当访问无权路由时,展示如下:

【React】路由鉴权-LMLPHP

每个页面都需要调用的函数

假如某些接口数据作用于全局且总会改变,如用户信息等,也可以写到AuthRouter中。
注意:只有处于登录态需要调用该函数的路由,才能调用。

AuthRouter

import { Navigate, useLocation } from "react-router-dom"
import API from "@/api"

const AuthRouter = (props: { children: JSX.Element }) => {
  const { pathname } = useLocation()

  // 不需要获取用户信息的路由
  const notGetUserInfoRouteList: string[] = [
    "/login",
    "/register",
    "/test/test4",
  ]

  let token = "dsnfoiwne23"
  if (!token) {
    return <Navigate to="/login" replace />
  } else {
    // 有token 且 需要调用
    if (!notGetUserInfoRouteList.includes(pathname)) {
      API.getUserInfo().then(({ code, data }) => {
        ...
      })
    }
    return props.children
  }
}

export default AuthRouter

上面的API是封装好的请求,具体可看另一篇文章:TS封装axios并约束请求参数以及响应的类型

整体代码

import { Navigate, useLocation } from "react-router-dom"
import cookie from "react-cookies"
import API from "@/api"

const AuthRouter = (props: { children: JSX.Element }) => {
  const { pathname } = useLocation()

  const whiteList: string[] = ["/login", "/test/test4"] // 白名单(无需登录)
  const noUserInfoList: string[] = [...whiteList, "/register"] // 不需要调用 getuserinfo 的路由
  const authList: string[] = ["/login", "/dashboard"] // 权限只能访问这些路由

  if (!authList.includes(pathname)) {
    return (
      <div>
        <h2>无权访问该页面</h2>
      </div>
    )
  }
  // 如果当前路由不在白名单中,且没有 token,则重定向到登录页
  if (!whiteList.includes(pathname) && !cookie.load("token")) {
    return <Navigate to="/login" replace />
  }

  // 如果当前路由需要调用 getuserinfo 接口,就调用它
  if (!noUserInfoList.includes(pathname)) {
    API.getUserInfo().then(({ code, data }) => {
      ...
    })
  }

  // 返回子组件
  return props.children
}

export default AuthRouter
04-12 03:47