JavaRush /Курсы /Модуль 3: React /Подключение аутентификации к GraphQL-запросам

Подключение аутентификации к GraphQL-запросам

Модуль 3: React
12 уровень , 8 лекция
Открыта

Подключение токенов аутентификации в Apollo Client

Добавление токенов в заголовки

Чтобы передать токен аутентификации с каждым запросом, нам нужно настроить Apollo Client для автоматического добавления токена в заголовки. Для этого мы используем Apollo Link.

import { ApolloClient, InMemoryCache, HttpLink, ApolloLink } from '@apollo/client';

// Создаем HttpLink для подключения к GraphQL-серверу
const httpLink = new HttpLink({
  uri: 'https://your-graphql-api.com/graphql',
});

// Создаем ApolloLink для добавления токенов в заголовки
const authLink = new ApolloLink((operation, forward) => {
  // Получаем токен из localStorage
  const token = localStorage.getItem('authToken');

  // Добавляем заголовок Authorization к запросу
  operation.setContext({
    headers: {
      Authorization: token ? `Bearer ${token}` : '',
    },
  });

  return forward(operation);
});

// Настраиваем Apollo Client
const client = new ApolloClient({
  link: ApolloLink.from([authLink, httpLink]),
  cache: new InMemoryCache(),
});

export default client;

Что мы сделали:

  • Использовали ApolloLink, чтобы изменить контекст запроса перед отправкой.
  • Добавили токен, полученный из localStorage, в заголовок Authorization.
Примечание:

хранение токенов в localStorage — это простое решение, подходящее для небольших проектов. Однако в реальных проектах токены часто хранят в HTTP-only cookies для повышения безопасности.

Интеграция ApolloProvider в приложение

Теперь мы должны внедрить Apollo Client в наше приложение через ApolloProvider, чтобы компоненты могли использовать клиент.

import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from '@apollo/client';
import client from './apolloClient'; // Импортируем нашего клиента

import App from './App';

ReactDOM.render(
  <ApolloProvider client={client}>
    <App />
  </ApolloProvider>,
  document.getElementById('root'),
);

Теперь App и все его дочерние компоненты могут отправлять аутентифицированные запросы.

Управление состоянием аутентификации

Чтобы эффективно работать с токенами аутентификации, мы добавим механизм для их хранения, обновления и удаления. Для этого создадим AuthContext.

1. Создаем контекст аутентификации

import React, { createContext, useContext, useState } from 'react';

// Интерфейс для описания состояния аутентификации
interface AuthContextType {
  token: string | null;
  login: (newToken: string) => void;
  logout: () => void;
}

// Создаем контекст
const AuthContext = createContext<AuthContextType | undefined>(undefined);

export const AuthProvider: React.FC = ({ children }) => {
  const [token, setToken] = useState<string | null>(localStorage.getItem('authToken'));

  const login = (newToken: string) => {
    setToken(newToken);
    localStorage.setItem('authToken', newToken);
  };

  const logout = () => {
    setToken(null);
    localStorage.removeItem('authToken');
  };

  return (
    <AuthContext.Provider value={{ token, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
};

// Хук для использования контекста
export const useAuth = (): AuthContextType => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within an AuthProvider');
  }
  return context;
};

Что мы сделали:

  • Создали AuthContext для хранения токена.
  • Реализовали методы login и logout для управления токеном.
  • Настроили хук useAuth для удобного доступа к контексту.

Внедрение AuthProvider

Оборачиваем App в AuthProvider:

import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from '@apollo/client';

import App from './App';
import client from './apolloClient';
import { AuthProvider } from './AuthProvider';

ReactDOM.render(
  <ApolloProvider client={client}>
    <AuthProvider>
      <App />
    </AuthProvider>
  </ApolloProvider>,
  document.getElementById('root'),
);

Теперь наш контекст доступен во всем приложении.

Пример использования аутентификации в компоненте

Представим, что у нас есть форма входа, где пользователь вводит свои учетные данные. После успешного входа мы сохраняем токен.

import React, { useState } from 'react';
import { useAuth } from './AuthProvider';

const LoginForm: React.FC = () => {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const { login } = useAuth(); // Достаем метод login из нашего контекста

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();

    // Пример запроса на сервер для получения токена
    const response = await fetch('https://your-api.com/login', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ username, password }),
    });

    if (response.ok) {
      const data = await response.json();
      login(data.token); // Сохраняем токен с помощью login
    } else {
      alert('Ошибка авторизации');
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        placeholder="Имя пользователя"
        value={username}
        onChange={(e) => setUsername(e.target.value)}
      />
      <input
        type="password"
        placeholder="Пароль"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
      />
      <button type="submit">Войти</button>
    </form>
  );
};

export default LoginForm;

Теперь, после успешного входа, все запросы будут автоматически содержать токен аутентификации в заголовке Authorization.

Защита маршрутов

Мы можем ограничить доступ к определенным маршрутам только для авторизованных пользователей с помощью защищенного компонента.

import React from 'react';
import { useAuth } from './AuthProvider';
import { Navigate } from 'react-router-dom';

const ProtectedRoute: React.FC<{ children: React.ReactNode }> = ({ children }) => {
  const { token } = useAuth();

  if (!token) {
    return <Navigate to="/login" />;
  }

  return <>{children}</>;
};

export default ProtectedRoute;

Используем ProtectedRoute для защищенных страниц:

import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import ProtectedRoute from './ProtectedRoute';
import Dashboard from './Dashboard';
import LoginForm from './LoginForm';

const App: React.FC = () => {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/login" element={<LoginForm />} />
        <Route
                path="/dashboard"
                element={
            <ProtectedRoute>
              <Dashboard />
            </ProtectedRoute>
          }
        />
      </Routes>
    </BrowserRouter>
  );
};

export default App;

Теперь страница Dashboard доступна только для авторизованных пользователей.

Пример запроса с аутентификацией

Для выполнения GraphQL-запросов аутентифицированным пользователем достаточно использовать useQuery или useMutation, так как токен уже добавлен в заголовки:

import { useQuery, gql } from '@apollo/client';

const GET_USER_INFO = gql`
  query GetUserInfo {
    user {
      id
      name
      email
    }
  }
`;

const UserInfo: React.FC = () => {
  const { data, loading, error } = useQuery(GET_USER_INFO);

  if (loading) return <p>Загрузка...</p>;
  if (error) return <p>Ошибка: {error.message}</p>;

  return (
    <div>
      <p>Имя: {data.user.name}</p>
      <p>Email: {data.user.email}</p>
    </div>
  );
};

export default UserInfo;

Этот запрос будет выполнен с заголовком Authorization.

Теперь наше приложение не только поддерживает аутентификацию, но и обеспечивает безопасность данных пользователей!

1
Задача
Модуль 3: React, 12 уровень, 8 лекция
Недоступна
Настройка Apollo Client с токенами аутентификации
Настройка Apollo Client с токенами аутентификации
1
Задача
Модуль 3: React, 12 уровень, 8 лекция
Недоступна
Реализация AuthContext
Реализация AuthContext
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ