Подключение токенов аутентификации в 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.
Теперь наше приложение не только поддерживает аутентификацию, но и обеспечивает безопасность данных пользователей!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ