关于javascript:在-React-Router-中使用-JWT

3次阅读

共计 9412 个字符,预计需要花费 24 分钟才能阅读完成。

hello 大家好,我是 superZidan,这篇文章想跟大家聊聊 在 React Router 中应用 JWT,如果大家遇到任何问题,欢送 分割我 或者间接微信增加 superZidan41

在这篇文章中,咱们将探讨 JWT 身份校验与 React 和 React-router 的无缝集成。咱们还将学习如何解决公共路由、受校验爱护路由,以及如何利用 axios 库通过身份验证令牌(token)收回 API 申请。

创立一个 React 我的项目

应用下方的指令会为咱们创立一个我的项目

$ npm create vite@latest react-jwt-cn

而后咱们抉择 reactjavascript 作为咱们的框架和语言。在我的项目开始之前,咱们要确保所有的依赖都曾经被装置,所以咱们要先执行

$ npm install

装置结束后,在我的项目的根目录下,咱们能够运行上面的指令来启动咱们的我的项目

$ npm run dev

咱们通过这些步骤来让咱们的 React 我的项目顺利启动和运行

装置 React-Router 和 Axios

在咱们持续之前,要确保咱们曾经为咱们的我的项目装置了必要的依赖项。咱们将从装置 react-router v6 开始,它将解决咱们的 React 应用程序中的路由。此外,咱们将装置 Axios,这是一个用于发送 API 申请的库。通过执行这些步骤,咱们将装备实现无缝路由和执行高效 API 通信所需的工具。让咱们从装置这些依赖项开始。

$ npm install react-router-dom axios

在 React 中创立 AuthProvider 和 AuthContext

接下来咱们要实现的就是 JWT 身份验证的性能。在这个大节中咱们将创立一个 AuthProvider 组件和一个关联的 AuthContext。这将帮助咱们在整个利用中存储和共享 JWT 身份验证相干的数据和函数

src > provider 下创立 authProvider.js。而后咱们来探 AuthProvider 和 AuthContext 的实现

  1. 导入必要的模块和依赖包:

    1. 导入 axios 用于发送 API 申请
    2. react 导入 createContext useContext useEffect useMemo 以及 useState
import axios from "axios";
import {
  createContext,
  useContext,
  useEffect,
  useMemo,
  useState,
} from "react";
  1. 应用 createContext() 来创立一个用于身份验证的上下文

    1. createContext() 创立的空的上下文是用于在组件之间共享身份验证的数据和函数的
const AuthContext = createContext();
  1. 创立 AuthProvider 组件

    1. 这个组件是用于作为身份验证上下文 的 provider
    2. 它接管 children 作为 prop,代表将有权拜访身份验证上下文的子组件。
const AuthProvider = ({children}) => {// 组件内容写在这里};
  1. 应用 useState 定义一个名为 token 的 state

    1. token 代表的是身份验证的令牌
    2. 如果令牌数据存在的话,咱们将通过 localStorage.getItem("token") 来获取它
const [token, setToken_] = useState(localStorage.getItem("token"));
  1. 创立 setToken 函数来更新身份验证的令牌数据

    1. 这个函数将会用于更新身份验证的令牌
    2. 它应用 setToken_ 函数更新令牌数据并且将更新之后的数据通过 localStorage.setItem() 存储在本地环境
const setToken = (newToken) => {setToken_(newToken);
};
  1. 应用 useEffect() 来设置 axios 默认的身份验证申请头并且将身份验证的令牌数据保留到本地

    1. 每当 token 更新,这个 effect 函数都会执行
    2. 如果 token 存在,它将被设置为 axios 的申请头并且保留到本地 localStorage 中
    3. 如果 token 是 null 或者 undefined,它将移除对应的 axios 申请头以及本地身份验证相干的 localStorage 的数据
useEffect(() => {if (token) {axios.defaults.headers.common["Authorization"] = "Bearer" + token;
    localStorage.setItem('token',token);
  } else {delete axios.defaults.headers.common["Authorization"];
    localStorage.removeItem('token')
  }
}, [token]);
  1. 应用 useMemo 创立记忆化的上下文

    1. 这个上下文蕴含 tokensetToken 函数
    2. token 的值会被作为记忆化的依赖项(如果 token 不变,则不会从新渲染)
const contextValue = useMemo(() => ({
    token,
    setToken,
  }),
  [token]
);
  1. 给自组件注入身份验证的上下文

    1. 应用 AuthContext.Provider 包裹子组件
    2. 把 contextValue 作为 provider 的值传入
return (<AuthContext.Provider value={contextValue}>
    {children}
  </AuthContext.Provider>
);
  1. 导出 useAuth 这个 hook,以供内部应用到身份验证这个 context

    1. useAuth 是一个自定义的 hook,它能够让子组件很不便的拜访到身份验证信息
export const useAuth = () => {return useContext(AuthContext);
};
  1. 默认导出 AuthProvider
export default AuthProvider;

残缺代码

import axios from "axios";
import {
  createContext,
  useContext,
  useEffect,
  useMemo,
  useState,
} from "react";

const AuthContext = createContext();

const AuthProvider = ({children}) => {const [token, setToken_] = useState(localStorage.getItem("token"));

    const setToken = (newToken) => {setToken_(newToken);
    };

    useEffect(() => {if (token) {axios.defaults.headers.common["Authorization"] = "Bearer" + token;
          localStorage.setItem('token',token);
        } else {delete axios.defaults.headers.common["Authorization"];
          localStorage.removeItem('token')
        }
    }, [token]);
    
    const contextValue = useMemo(() => ({
          token,
          setToken,
        }),
        [token]
    );

    return (<AuthContext.Provider value={contextValue}>
          {children}
        </AuthContext.Provider>
    );

};

export const useAuth = () => {return useContext(AuthContext);
};
  
export default AuthProvider;

小结,此代码应用 React 的 context API 设置身份验证上下文。它通过 context 向子组件提供身份验证令牌和 setToken 函数。它还确保在身份验证令牌更新时能够及时更新 axios 中的默认受权申请头。

为 JWT 身份验证创立路由

为了可能更高效的组织路由,咱们将创立一个 src > routes 目录。在这个目录里,咱们将创立一个 index.jsx 文件,这个文件用来作为定义整个利用路由的入口。通过在独自的文件夹中构建咱们的路由,咱们能够放弃清晰且易于治理的路由构造。让咱们持续创立路由并摸索如何将 JWT 身份验证集成到咱们的 React 应用程序中。

为身份验证路由创立受爱护路由组件

为了爱护咱们身份验证的路由并避免未经受权的拜访,咱们将创立一个名为 ProtectedRoute 的组件。这个组件将包裹咱们的身份验证路由,以确保只有被受权的用户才可能拜访。通过事实这个组件,咱们能够轻松实现身份验证需要并提供良好的用户体验。咱们将在 src > routes 下创立 ProtectedRoute.jsx 文件

  1. 首先咱们要从 react-router-dom 中导入必要的依赖
import {Navigate, Outlet} from "react-router-dom";
import {useAuth} from "../provider/authProvider";
  1. 定义 ProtectedRoute 组件,让它包裹咱们所有的须要鉴权的路由
export const ProtectedRoute = () => {const { token} = useAuth();
  
    // 判断用户是否有权限
    if (!token) {
      // 如果没有受权,则跳转到登录页面
      return <Navigate to="/login" />;
    }
  
    // 如果曾经受权,则间接渲染子组件
    return <Outlet />;
 };
  1. ProtectedRoute 组件中,咱们通过 AuthContext 提供的自定义 hook(useAuth)来获取 token 信息
  2. 接下来咱们查看 token 是否存在。如果用户没有被受权(token 是 faslse 或者是 null),咱们将把路由导航到登录页面(/login
  3. 如果用户被受权了,咱们将应用 Outlet 组件来渲染子路由。Outlet 组件充当占位符,显示父路由中定义的子组件。

小结,ProtectedRoute 组件充当了身份验证的路由的守卫。如果用户未通过身份验证,他们将被重定向到登录页面。如果用户通过身份验证,则 ProtectedRoute 组件中定义的子路由将应用 Outlet 组件出现。

上述代码使咱们可能依据用户的身份验证状态轻松爱护特定路由并管制拜访,从而在咱们的 React 应用程序中提供平安的导航体验。

深刻摸索路由

当初咱们曾经有了 ProtectedRoute 组件和身份验证上下文,咱们能够持续定义咱们的路由。通过辨别公共路由、受校验爱护路由和非认证用户路由,咱们能够无效地解决基于 JWT 认证的导航和访问控制。接下来咱们将深刻到 src > routes > index.jsx 文件并摸索如何将 JWT 身份校验集成到咱们的路由构造中

  1. 导入必要的依赖

    1. RouterProvidercreateBrowserRouter 用于配置和提供路由性能
    2. useAuth 运行咱们拜访身份校验的上下文
    3. ProtectedRoute 组件包裹着受校验路由
import {RouterProvider, createBrowserRouter} from "react-router-dom";
import {useAuth} from "../provider/authProvider";
import {ProtectedRoute} from "./ProtectedRoute";
  1. 定义路由组件

    1. 该函数组件充当配置应用程序路由的入口
const Routes = () => {const { token} = useAuth();
  // 路由配置写在这里
};
  1. 应用 useAuth hook 拜访身份校验令牌

    1. 调用 useAuth hook 能够从身份校验上下文中获取令牌
const {token} = useAuth();
  1. 定义面向所有用户的路由(公共路由)

    1. routesForPublic 数组爱护所有可被所有用户拜访的路由信息。每个路由信息对象蕴含一个 path 和一个 element
    2. path 属性明确了路由的 URL 门路,element 属性指向该路由下须要渲染的 jsx 组件 / 元素
const routesForPublic = [
  {
    path: "/service",
    element: <div>Service Page</div>,
  },
  {
    path: "/about-us",
    element: <div>About Us</div>,
  },
];
  1. 定义只有受权用户能够拜访的路由

    1. routesForAuthenticatedOnly 数组蕴含只能由通过身份验证的用户拜访的路由对象。它包含包装在 ProtectedRoute 组件中的受爱护根路由(“/”)和应用 children 属性定义的其余子路由。
const routesForAuthenticatedOnly = [
  {
    path: "/",
    element: <ProtectedRoute />,
    children: [
      {
        path: "/",
        element: <div>User Home Page</div>,
      },
      {
        path: "/profile",
        element: <div>User Profile</div>,
      },
      {
        path: "/logout",
        element: <div>Logout</div>,
      },
    ],
  },
];
  1. 定义只有没有受权的用户才能够拜访的路由

    1. routesForNotAuthenticatedOnly 数组蕴含没有通过身份验证的用户拜访的路由对象。它蕴含登录路由(/login
const routesForNotAuthenticatedOnly = [
  {
    path: "/",
    element: <div>Home Page</div>,
  },
  {
    path: "/login",
    element: <div>Login</div>,
  },
];
  1. 基于身份验证状态来组合和判断路由

    1. createBrowserRouter 函数用于创立路由配置,它接管一个路由数组作为入参
    2. 扩大运算符 (…) 用于将多个路由数组合并到一个数组
    3. 条件表达式 (!token ? routesForNotAuthenticatedOnly : []) 检查用户是否已通过身份验证(令牌存在)。如果不是,则蕴含 routesForNotAuthenticatedOnly 数组;否则,它蕴含一个空数组。
const router = createBrowserRouter([
  ...routesForPublic,
  ...(!token ? routesForNotAuthenticatedOnly : []),
  ...routesForAuthenticatedOnly,
]);
  1. 应用 RouterProvider 注入路由配置

    1. RouterProvider 组件包装路由配置,使其可用于整个应用程序
return <RouterProvider router={router} />;

残缺代码

import {RouterProvider, createBrowserRouter} from "react-router-dom";
import {useAuth} from "../provider/authProvider";
import {ProtectedRoute} from "./ProtectedRoute";

const Routes = () => {const { token} = useAuth();

  // 公共路由配置
  const routesForPublic = [
    {
      path: "/service",
      element: <div>Service Page</div>,
    },
    {
      path: "/about-us",
      element: <div>About Us</div>,
    },
  ];

  // 受权的用户才能够拜访的路由配置
  const routesForAuthenticatedOnly = [
    {
      path: "/",
      element: <ProtectedRoute />, // Wrap the component in ProtectedRoute
      children: [
        {
          path: "/",
          element: <div>User Home Page</div>,
        },
        {
          path: "/profile",
          element: <div>User Profile</div>,
        },
        {
          path: "/logout",
          element: <div>Logout</div>,
        },
      ],
    },
  ];

  // 没有受权的用户才能够拜访的路由配置
  const routesForNotAuthenticatedOnly = [
    {
      path: "/",
      element: <div>Home Page</div>,
    },
    {
      path: "/login",
      element: <div>Login</div>,
    },
  ];

  // 合并路由配置
  const router = createBrowserRouter([
    ...routesForPublic,
    ...(!token ? routesForNotAuthenticatedOnly : []),
    ...routesForAuthenticatedOnly,
  ]);

  return <RouterProvider router={router} />;
};

export default Routes;

最初整合

当初咱们曾经筹备好了 AuthContextAuthProvider  和  Routes。让咱们把它们整合到 App.jsx

  1. 导入必要的组件和文件

    1. AuthProvider 是从 ./provider/authProvider 文件中导入的组件。它为整个应用程序提供了身份验证的上下文
    2. ./routes 中导入 Routes。它定义了利用路由
import AuthProvider from "./provider/authProvider";
import Routes from "./routes";
  1. 应用 AuthProvider 组件包装 Routes 组件

    1. AuthProvider 组件用于向应用程序提供身份验证上下文。它包装了 Routes 组件,使身份验证上下文可用于 Routes 组件树中的所有组件
return (
  <AuthProvider>
    <Routes />
  </AuthProvider>
);

残缺代码

import AuthProvider from "./provider/authProvider";
import Routes from "./routes";

function App() {
  return (
    <AuthProvider>
      <Routes />
    </AuthProvider>
  );
}

export default App;

实现登录与登出

src > pages > Login.jsx 创立 登录页面

const Login = () => {const { setToken} = useAuth();
  const navigate = useNavigate();

  const handleLogin = () => {setToken("this is a test token");
    navigate("/", { replace: true});
  };

  setTimeout(() => {handleLogin();
  }, 3 * 1000);

  return <>Login Page</>;
};

export default Login;
  • 登录组件是一个用于示意登录页面的函数组件
  • 应用 useAuth hook 从身份校验上下文中导入 setToken 函数
  • react-router-dom 中导入 navigate 函数用于解决路由跳转
  • 在组件外部,有一个 handleLogin 函数,它应用上下文中的 setToken 函数设置测试令牌,并导航到主页 (“/”),并将替换选项(replace)设置为 true
  • setTimeout 函数用于模仿执行 handleLogin 函数前的 3 秒提早
  • 组件为登录页返回 JSX,在此处充当一个占位符文本

当初,咱们在 src > pages > Logout.jsx 创立一个 登出页面

import {useNavigate} from "react-router-dom";
import {useAuth} from "../provider/authProvider";

const Logout = () => {const { setToken} = useAuth();
  const navigate = useNavigate();

  const handleLogout = () => {setToken();
    navigate("/", { replace: true});
  };

  setTimeout(() => {handleLogout();
  }, 3 * 1000);

  return <>Logout Page</>;
};

export default Logout;
  • 在登出页面中,咱们调用了 setToken 函数并且没有传参,这相当于调用 setToken(null)

当初,咱们将用更新后的版本替换路由组件中的登录和登出组件

const routesForNotAuthenticatedOnly = [
  {
    path: "/",
    element: <div>Home Page</div>,
  },
  {
    path: "/login",
    element: <Login />,
  },
];

routesForNotAuthenticatedOnly 数组中,“/login”element 属性设置为 <Login />,示意当用户拜访 “/login” 门路时,会渲染 Login 组件

const routesForAuthenticatedOnly = [
  {
    path: "/",
    element: <ProtectedRoute />,
    children: [
      {
        path: "/",
        element: <div>User Home Page</div>,
      },
      {
        path: "/profile",
        element: <div>User Profile</div>,
      },
      {
        path: "/logout",
        element: <Logout />,
      },
    ],
  },
];

routesForAuthenticatedOnly 数组中,“/logout”element 属性设置为 <Logout />,示意当用户拜访 “/logout” 门路时,会渲染 Logout 组件

测试流程

  1. 当你第一次拜访根页面 / 时,会看到 routesForNotAuthenticatedOnly 数组中的“Home page”
  2. 如果你导航到 /login,在提早 3 秒后,将模仿登录过程。它将应用身份验证上下文中的 setToken 函数设置测试令牌,而后你将被 react-router-dom 库中的导航函数重定向到根页面 /。重定向后,你将从 routesForAuthenticatedOnly 数组中看到“User Home Page”
  3. 如果你随后拜访 /logout,在提早 3 秒后,将模仿登出过程。它将通过不带任何参数调用 setToken 函数来革除身份验证令牌,而后您将被重定向到根页面 /。因为你当初已登出,咱们将从 routesForNotAuthenticatedOnly 数组中看到“Home Page”。

此流程演示了登录和登出过程,其中用户在通过身份验证和未通过身份验证的状态之间转换,并相应地显示相应的路由。

以上就是本篇文章的全部内容,感激大家对本文的反对~欢送点赞珍藏,在评论区留下你的浅见 🌹🌹🌹

其余

  • 本文为翻译文,原文地址 在这里
  • 代码仓库
正文完
 0