Зачем нужно генерировать и валидировать JWT
Для чёткого понимания давайте разберём сценарий. Представьте, что вы посетили веб-приложение (например, интернет-магазин). После успешного логина сервер выдаёт вам уникальный токен (JWT), который будет отправляться вместе с каждым запросом. Сервер, в свою очередь, использует этот токен для проверки вашей идентификации. Если токен валиден — сервер говорит: "Окей, могу доверять этому человеку". Если нет — вы попадёте на страницу логина.
Как работает создание JWT (немного теории перед практикой)
Создание токена:
- Сервер генерирует JWT, подписывая его секретным ключом.
- Токен включает полезную нагрузку с данными о пользователе (например,
id,email,роль). - Токен имеет срок действия
exp, после которого становится недействительным.
Валидация токена:
- Каждый раз, когда клиент отправляет запрос с токеном, сервер проверяет подпись и срок действия.
- Если подпись или срок невалидны — доступ закрывается.
Библиотека для работы с JWT
Для работы с JWT на сервере мы будем использовать популярную библиотеку jsonwebtoken. Она проста, удобна и хорошо документирована.
Установка
npm install jsonwebtoken
Не бойтесь, установка миниатюрная. В отличие от "node_modules", которая, кажется, весит больше, чем половина интернета.
Генерация JWT на сервере
Перейдём от слов к делу. Для начала создадим простое Express-приложение. Если у вас ещё нет проекта, создайте его:
mkdir jwt-auth-app
cd jwt-auth-app
npm init -y
npm install express jsonwebtoken body-parser
Создайте файл server.ts и напишите следующий код:
server.ts
import express, { Request, Response } from "express";
import jwt from "jsonwebtoken";
const app = express();
const PORT = 3000;
// Секретный ключ для подписи токенов
const JWT_SECRET = "supersecretkey"; // Никогда не используйте такую строчку в реальных проектах, храните секреты в .env.
app.use(express.json());
// Пример эндпоинта для генерации токена
app.post("/login", (req: Request, res: Response) => {
const { username, password } = req.body;
// Простая проверка данных (не для продакшена)
if (username === "admin" && password === "12345") {
// Полезная нагрузка токена
const payload = { username };
// Генерация токена
const token = jwt.sign(payload, JWT_SECRET, { expiresIn: "1h" });
return res.json({ token });
}
res.status(401).json({ message: "Invalid username or password" });
});
// Запуск сервера
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT}`));
Разбор кода
jwt.sign: используется для генерации токена. Первый аргумент — это полезная нагрузка, второй — секретный ключ, а третий — настройки (например, срок действия).- Секретный ключ: это строка, которая используется для подписи токена. Никогда не храните её в коде, всегда используйте переменные окружения.
- Временный срок действия
expiresIn: "1h": указывает, что токен будет действителен в течение 1 часа.
Теперь запустите сервер командой node server.ts (не забудьте компилировать TypeScript, если используете его). Отправьте POST-запрос на /login с телом:
{
"username": "admin",
"password": "12345"
}
Если всё работает правильно, вы получите токен. Поздравляю!
Валидация JWT на сервере
Собственно, теперь пришло время научиться проверять токены. Добавим новый защищённый маршрут /protected, который будет доступен только аутентифицированным пользователям.
server.ts
// Middleware для проверки токена
const authenticateToken = (req: Request, res: Response, next: Function) => {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1]; // Bearer <token>
if (!token) {
return res.status(401).json({ message: "Access token is missing" });
}
try {
// Проверяем токен
const payload = jwt.verify(token, JWT_SECRET);
(req as any).user = payload; // Записываем данные в запрос
next();
} catch (error) {
return res.status(403).json({ message: "Invalid token" });
}
};
// Защищённый маршрут
app.get("/protected", authenticateToken, (req: Request, res: Response) => {
res.json({ message: `Welcome, ${(req as any).user.username}!` });
});
Разбор кода
jwt.verify: проверяет токен на валидность, используя секретный ключ.- Bearer-токены: это стандартный способ передачи токенов в заголовках.
- Middleware: используется для проверки токена перед тем, как пользователь получит доступ к защищённому ресурсу.
Теперь попробуйте отправить GET-запрос на /protected с заголовком:
Authorization: Bearer <ваш-токен>
Если токен валиден, вы получите сообщение приветствия. Если нет — ошибку.
Типичные ошибки
- Пропущенный токен: сервер вернёт ошибку
Access token is missing, если в запросе отсутствует токен. - Истёкший токен: если время жизни токена истекло,
jwt.verifyвыбросит ошибку. - Неверный секретный ключ: если токен был подписан другим ключом, сервер не сможет его проверить.
- Хранение токена в JavaScript-коде: никогда не храните секреты в коде! Используйте
.env-переменные.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ