Shane Jix

[Snippet] - Authentication in React Router

create:January 19, 2022  update:April 12, 2022  ☕️ 4 min read

同步链接: https://www.shanejix.com/posts/[Snippet] - Authentication in React Router/

React Router V5

file tree

.
├── conponents
    └── Authentication.tsx
├── pages
    ├── Home.tsx
    ├── Login.tsx
    └── Management.tsx
├── routes
    ├── privateRoutes.tsx
    └── publicRoutes.tsx
├── App.tsx

App.tsx

import React, { useState } from "react";
import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import publicRoutes from "./routes/publicRoutes";
import privateRoutes from "./routes/privateRoutes";
import Authentication from "./components/Authentication";

function App() {
  const [user, setUser] = useState({});

  const loginAsUser = () => {
    setUser({
      role: ["user"],
    });
  };

  return (
    <Router>
      <Switch>
        {publicRoutes.map(({ path, component: Component, ...route }) => (
          <Route
            key={path}
            path={path}
            render={(routeProps: any) => (
              <Component loginAsUser={loginAsUser} {...routeProps} />
            )}
            {...route}
          />
        ))}
        {privateRoutes.map((route) => (
          <Authentication key={route.path} {...route} user={user} />
        ))}
      </Switch>
    </Router>
  );
}

export default App;

publicRoutes.ts

import Login from "../pages/Login";
import Home from "../pages/Home";

const publicRoutes = [
  {
    path: "/login",
    component: Login,
    exact: true,
  },
  {
    path: "/",
    component: Home,
    exact: true,
  },
];

export default publicRoutes;

privateRoutes.ts

import Management from "../pages/Management";

const privateRoutes = [
  {
    path: "/management",
    component: Management,
    exact: true,
    role: "user",
    backUrl: "/login",
  },
];

export default privateRoutes;

Authentication.tsx

import React from "react";
import { Route, Redirect } from "react-router-dom";

function Authentication(props: any) {
  const {
    user: { role: userRole },
    role: routeRole,
    backUrl,
    ...otherProps
  } = props;

  // 如果用户有权限,就渲染对应的路由
  if (userRole && userRole.includes(routeRole)) {
    return <Route {...otherProps} />;
  } else {
    // 如果没有权限,返回配置的默认路由
    return <Redirect to={backUrl} />;
  }
}

export default Authentication;

注意:react-router-dom 所依赖的版本为 “^5.3.3”,完整代码

React Router V6

file tree

├── conponents
    ├── AuthProvider.jsx
    ├── Navgation.jsx
    └── ProtectedRoute.tsx
├── hooks
    ├── useAuth.jsx
├── pages
    ├── Admin.jsx
    ├── Dashboard.jsx
    ├── Home.jsx
    └── NoMatch.jsx
├── stores
    ├── AuthContext.jsx
├── utils
    ├── fakeAuth.js
├── App.jsx
├── index.jsx
├── Router.jsx

App.jsx

import * as React from "react";
import AuthProvider from "./components/AuthProvider";
import Navigation from "./components/Navigation";
import Router from "./Router";

const App = () => {
  return (
    <AuthProvider>
      <h1>React Router V6</h1>
      <Navigation />
      <Router />
    </AuthProvider>
  );
};

export default App;

Router.jsx

import * as React from "react";
import { Routes, Route } from "react-router-dom";
import ProtectedRoute from "./components/ProtectedRoute";
import Home from "./pages/Home";
import Dashboard from "./pages/Dashboard";
import Admin from "./pages/Admin";
import NoMatch from "./pages/NoMatch";

const Router = () => {
  return (
    <Routes>
      <Route index element={<Home />} />
      <Route path="home" element={<Home />} />
      <Route
        path="dashboard"
        element={
          <ProtectedRoute>
            <Dashboard />
          </ProtectedRoute>
        }
      />
      <Route
        path="admin"
        element={
          <ProtectedRoute>
            <Admin />
          </ProtectedRoute>
        }
      />
      <Route path="*" element={<NoMatch />} />
    </Routes>
  );
};

export default Router;

AuthProvider.jsx

import * as React from "react";
import { useNavigate, useLocation } from "react-router-dom";
import AuthContext from "../stores/AuthContext";
import fakeAuth from "../utils/fakeAuth";

const AuthProvider = ({ children }) => {
  const navigate = useNavigate();
  const location = useLocation();

  const [token, setToken] = React.useState(null);

  const handleLogin = async () => {
    const token = await fakeAuth();

    setToken(token);

    const origin = location.state?.from?.pathname || "/dashboard";
    navigate(origin);
  };

  const handleLogout = () => {
    setToken(null);
  };

  const value = {
    token,
    onLogin: handleLogin,
    onLogout: handleLogout,
  };

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

export default AuthProvider;

ProtectedRoute.jsx

import * as React from "react";
import { Navigate, useLocation } from "react-router-dom";
import useAuth from "../hooks/useAuth";

const ProtectedRoute = ({ children }) => {
  const { token } = useAuth();
  const location = useLocation();

  if (!token) {
    return <Navigate to="/home" replace state={{ from: location }} />;
  }

  return children;
};

export default ProtectedRoute;

注意:react-router-dom 所依赖的版本为 “^6.0.1”,完整代码

作者:shanejix 出处:https://www.shanejix.com/posts/[Snippet] - Authentication in React Router/ 版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。 声明:转载请注明出处!

Edit on GitHubDiscuss on GitHub


Shane Jix

Personal blog by Shane Jix. I explain with words and code.

LinksTools
© 2019 - 2022, Built withGatsby