Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Авторизация через JWT (JSON Web Token)
JWT - это стандартный способ передачи информации об аутентификации между сервером и клиентом. Это один из самых популярных методов авторизации в современных веб-приложениях.
Как работает JWT
JWT состоит из трех частей, разделенных точками:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
1. HEADER (заголовок)
2. PAYLOAD (данные)
3. SIGNATURE (подпись)
Каждая часть закодирована в Base64.
1. Структура JWT
Header (Заголовок)
// Расшифрованный header:
{
"alg": "HS256", // Алгоритм подписи
"typ": "JWT" // Тип токена
}
// Кодированный: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload (Данные)
// Расшифрованный payload:
{
"sub": "user-123", // Subject (ID пользователя)
"name": "John Doe", // Имя пользователя
"email": "john@example.com", // Email
"role": "admin", // Роль
"iat": 1516239022, // Issued at (когда выдан)
"exp": 1516325422 // Expiration (когда истекает)
}
Signature (Подпись)
// Подпись создается так:
SIGNATURE = HMACSHA256(
base64UrlEncode(HEADER) + "." +
base64UrlEncode(PAYLOAD),
SECRET_KEY
)
2. Процесс авторизации
Этап 1: Логин
// Клиент отправляет учетные данные
const login = async (email, password) => {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const data = await response.json();
// Сервер возвращает JWT токен
return data.token; // eyJhbGc...
};
Этап 2: Сохранение токена
// Клиент сохраняет токен
const handleLogin = async (email, password) => {
const token = await login(email, password);
// Вариант 1: localStorage (не очень безопасно)
localStorage.setItem('token', token);
// Вариант 2: httpOnly cookie (безопаснее)
// Сервер сам устанавливает cookie с флагом httpOnly
// Браузер автоматически отправляет cookie
// Вариант 3: sessionStorage (удаляется при закрытии)
sessionStorage.setItem('token', token);
};
Этап 3: Отправка токена в запросах
// При каждом API запросе добавляем токен в header
const apiCall = async (url, options = {}) => {
const token = localStorage.getItem('token');
const response = await fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${token}`
}
});
return response.json();
};
// Использование:
const userData = await apiCall('/api/users/me');
Этап 4: Проверка на сервере
// На сервере проверяют подпись и срок действия
const verifyJWT = (token, secretKey) => {
try {
// Декодируем и проверяем подпись
const decoded = jwt.verify(token, secretKey);
// Проверяем срок действия
if (decoded.exp < Date.now() / 1000) {
throw new Error('Token expired');
}
return decoded;
} catch (error) {
throw new Error('Invalid token');
}
};
3. Практическая реализация на фронте
// auth.ts - утилиты для работы с JWT
import { jwtDecode } from 'jwt-decode';
interface DecodedToken {
sub: string;
email: string;
role: string;
exp: number;
}
const TOKEN_KEY = 'auth_token';
export const authService = {
// Сохранить токен
setToken: (token: string) => {
localStorage.setItem(TOKEN_KEY, token);
},
// Получить токен
getToken: (): string | null => {
return localStorage.getItem(TOKEN_KEY);
},
// Удалить токен (выход)
removeToken: () => {
localStorage.removeItem(TOKEN_KEY);
},
// Проверить валидность
isTokenValid: (): boolean => {
const token = localStorage.getItem(TOKEN_KEY);
if (!token) return false;
try {
const decoded: DecodedToken = jwtDecode(token);
// Проверяем не истек ли токен
return decoded.exp * 1000 > Date.now();
} catch {
return false;
}
},
// Получить данные пользователя из токена
getUser: (): DecodedToken | null => {
const token = localStorage.getItem(TOKEN_KEY);
if (!token) return null;
try {
return jwtDecode(token);
} catch {
return null;
}
}
};
4. React Hook для авторизации
interface AuthContextType {
user: DecodedToken | null;
isAuthenticated: boolean;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
const AuthContext = createContext<AuthContextType | null>(null);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<DecodedToken | null>(null);
useEffect(() => {
// При загрузке проверяем есть ли валидный токен
if (authService.isTokenValid()) {
setUser(authService.getUser());
} else {
authService.removeToken();
}
}, []);
const login = async (email: string, password: string) => {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (!response.ok) throw new Error('Login failed');
const { token } = await response.json();
authService.setToken(token);
setUser(authService.getUser());
};
const logout = () => {
authService.removeToken();
setUser(null);
};
return (
<AuthContext.Provider
value={{
user,
isAuthenticated: !!user,
login,
logout
}}
>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
};
5. Защита маршрутов
// ProtectedRoute.tsx
interface ProtectedRouteProps {
children: React.ReactNode;
requiredRole?: string;
}
export function ProtectedRoute({
children,
requiredRole
}: ProtectedRouteProps) {
const { isAuthenticated, user } = useAuth();
if (!isAuthenticated) {
return <Navigate to="/login" />;
}
if (requiredRole && user?.role !== requiredRole) {
return <Navigate to="/unauthorized" />;
}
return <>{children}</>;
}
// Использование:
<Router>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route
path="/dashboard"
element={
<ProtectedRoute>
<Dashboard />
</ProtectedRoute>
}
/>
<Route
path="/admin"
element={
<ProtectedRoute requiredRole="admin">
<AdminPanel />
</ProtectedRoute>
}
/>
</Routes>
</Router>
6. Обновление токена (Refresh Token)
// JWT часто имеет короткий срок действия
// Для обновления используют refresh token
const refreshAccessToken = async () => {
const response = await fetch('/api/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
refreshToken: localStorage.getItem('refresh_token')
})
});
if (!response.ok) {
// Refresh не удалось - требуется повторная авторизация
window.location.href = '/login';
return;
}
const { token } = await response.json();
authService.setToken(token);
};
// Проверка и обновление перед каждым запросом
const apiCall = async (url: string, options: RequestInit = {}) => {
let token = authService.getToken();
// Если токен скоро истечет, обновляем
if (!authService.isTokenValid()) {
await refreshAccessToken();
token = authService.getToken();
}
return fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${token}`
}
});
};
7. Безопасность JWT
// ХОРОШО:
// 1. Отправляй токен только через HTTPS
// 2. Используй httpOnly cookies для хранения
// 3. Имей короткий срок действия (15-30 минут)
// 4. Используй refresh token для обновления
// 5. Проверяй подпись на сервере
// ПЛОХО:
// 1. Хранить токен в localStorage (уязвимо к XSS)
// 2. Передавать в URL параметрах
// 3. Не проверять срок действия
// 4. Использовать слабый secret key
// 5. Игнорировать HTTPS
JWT - это мощный и гибкий способ авторизации, но требует правильной реализации для обеспечения безопасности.