Как защитить маршрут с помощью токена в React с использованием Context API и React Cookies


Что будет сделано?

Мы научимся защищать маршрут с помощью JWT-аутентификации в react-router-dom v6. Маршруты будут доступны только тогда, когда у пользователей есть токен, сохраненный в cookies (или локальном хранилище).

Используемые технологии

  • React ^18.2.0;
  • JWT;
  • Axios ^0.27.2;
  • react-router-dom ^6.3.0;
  • react-cookie ^4.1.1;

Запуск

Давайте создадим приложение React

create-react-app protect-route-with-jwt
Вход в полноэкранный режим Выйти из полноэкранного режима

Эта команда создаст структуру проекта react с некоторыми необходимыми файлами. Удалите некоторые файлы и создайте некоторые папки, которые будут использоваться в проекте. Окончательная структура будет выглядеть следующим образом:

Теперь мы установим некоторые зависимости проекта, откроем терминал в папке проекта и выполним следующие команды:

yarn init
yarn add axios react-router-dom react-cookie
Войти в полноэкранный режим Выйти из полноэкранного режима

Определение API

Создайте файл в src > services > api.js, в котором мы определим базовый URL для выполнения запросов в нашем приложении:

import axios from 'axios';

const api = axios.create({
    baseURL: "http://localhost:8000" //your api URL
});

export default api;
Вход в полноэкранный режим Выход из полноэкранного режима

src > services > api.js

Мы определяем нашу переменную api из библиотеки axios и экспортируем ее.

Создание крючков

Перейдите в src > hooks, создав две папки, auth и protect Routes. Внутри них и папки hooks создайте файл index.js.

auth

import { createContext, useContext, useMemo } from 'react';
import { useCookies } from 'react-cookie';
import { useNavigate } from 'react-router-dom';
import api from '../../services/api';

const UserContext = createContext();

export const UserProvider = ({ children }) => {
    const navigate = useNavigate();
    const [cookies, setCookies, removeCookie] = useCookies();

    const login = async ({ email, password }) => {
        const res = await api.post('/auth', {
            email: email,
            password: password
        });

        setCookies('token', res.data.token); // your token
        setCookies('name', res.data.name); // optional data

        navigate('/home');
    };

    const logout = () => {
        ['token', 'name'].forEach(obj => removeCookie(obj)); // remove data save in cookies
        navigate('/login');
    };

    const value = useMemo(
        () => ({
            cookies,
            login,
            logout
        }),
        [cookies]
    );

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

export const useAuth = () => {
    return useContext(UserContext)
};
Вход в полноэкранный режим Выйти из полноэкранного режима

src > hooks > auth > index.js

По сути, здесь мы создаем пользовательский контекст, который имеет функции входа и выхода, и экспортируем его, чтобы его можно было использовать через дерево компонентов без необходимости передавать реквизиты вручную на каждом уровне. Если вы не понимаете, смотрите документацию по contextAPI.

protectRoutes

import { Outlet, Navigate } from 'react-router-dom';
import { useAuth } from '../auth';

export const ProtectRoutes = () => {
    const { cookies } = useAuth();

    return cookies.token ? <Outlet/> : <Navigate to='/login' exact />
};
Вход в полноэкранный режим Выйти из полноэкранного режима

src > hooks > protectRoutes > index.js

Здесь мы создаем защиту маршрута, используя наш useAuth, который мы определили ранее, если существует токен в cookies, то приложение следует за ним, иначе перенаправляет на страницу входа.

hooks

Создаем индекс провайдеров hooks, добавляя все провайдеры, которые мы будем использовать в приложении. В данном случае только хук auth, который будет использоваться в глобальном контексте приложения, поэтому просто импортируем и добавляем его.

import { UserProvider } from './auth';

const AppProvider = ({ children }) => (
    <>
        <UserProvider>{ children }</UserProvider>
    </>
);

export default AppProvider;
Вход в полноэкранный режим Выход из полноэкранного режима

src > hooks > index.js

Теперь все, что мы поместим внутрь AppProvider, будет иметь доступ к хуку useAuth.

Создание страниц

Создайте две страницы для этого примера, главную и страницу входа. Они будут расположены в src > pages:

Страницы примера будут выглядеть следующим образом:

import React from 'react';

export default function Home() {
    return <div>Home Page</div>
}

Вход в полноэкранный режим Выход из полноэкранного режима

src > pages > Home > index.js

import React, { useState } from 'react';
import { useAuth } from "../../hooks/auth";

export default function Login() {
    const [cnpj, setCnpj] = useState('');
    const [password, setPassword] = useState('');
    const { login } = useAuth();

    const handleLogin = () => {
        login({ cnpj, password });
    }

    return (
        <div>
            <input onChange={e => setCnpj(e.target.value)} placeholder="Email"/>
            <input onChange={e => setPassword(e.target.value)} placeholder="Password"/>
            <button onClick={handleLogin} type="submit">Login</button>
        </div>
    )
}
Вход в полноэкранный режим Выход из полноэкранного режима

src > pages > Login > index.js

Создание маршрутов

Перейдите в index.js папки src и добавьте следующий код:

import React from 'react';
import ReactDOM from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import AppProvider from './hooks';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <AppProvider>
        <App />
      </AppProvider>
    </BrowserRouter>
  </React.StrictMode>
);
Вход в полноэкранный режим Выход из полноэкранного режима

src > index.js

Давайте определим некоторые маршруты приложений и, наконец, защитим их. Перейдите в App.js и добавьте следующий код:

import React from 'react';
import { Route, Routes, Navigate } from 'react-router-dom';
import { ProtectRoutes } from './hooks/protectRoutes';
import Home from './pages/Home';
import Login from './pages/Login';

export default function App() {
  return (
    <Routes>
      <Route path='/' element={ <Navigate to='home' exact /> } />
      <Route path='/login' element={ <Login /> } />

      <Route element={ <ProtectRoutes /> }>
        <Route path='/home' element={ <Home /> } />
      </Route>
    </Routes>
  )
}
Вход в полноэкранный режим Выход из полноэкранного режима

src > App.js

Здесь мы определяем маршруты внутри компонента Routes. Чтобы добавить наш Protectd Route вокруг маршрутов, которые мы хотим защитить, достаточно открыть тег <Route> с нашим ProtectRoutes в качестве элемента. Внутри тега мы добавим маршруты, в данном случае мы защищаем домашний маршрут, то есть пользователь сможет получить к нему доступ только при наличии токена в cookies, что мы и определили в защите маршрута ранее.

Вот и все, теперь все маршруты, которые вам нужно защитить с помощью аутентификации пользователя, должны быть помещены в Route, которые имеют элемент <ProtectRoutes>.

Вы можете получить доступ к этому проекту на моем github: https://github.com/ViniBGoulart/protect-route-with-jwt

Оцените статью
devanswers.ru
Добавить комментарий