JWT авторизация в Node.js: защищаем REST API
Кратко

Когда вы создаёте приложение, важно защитить конфиденциальные данные от неавторизованного доступа. Многие современные веб-, мобильные и облачные приложения используют REST API как основной канал взаимодействия. Поэтому при проектировании и разработке серверных API безопасность должна быть приоритетом.
Один из распространённых и практичных способов защиты REST API — использование JSON Web Token (JWT). JWT обеспечивает удобный механизм аутентификации и авторизации, помогающий ограничивать доступ к защищённым ресурсам.
Что такое JSON Web Tokens
JSON Web Token (JWT) — это стандарт передачи утверждений (claims) в компактном и самодостаточном виде. JWT позволяет безопасно передавать информацию между клиентом и сервером.
REST API может использовать JWT для идентификации и аутентификации пользователя при отправке HTTP-запросов к защищённым маршрутам.

JSON Web Token состоит из трёх частей: header (заголовок), payload (полезная нагрузка) и signature (подпись). Каждая часть кодируется и объединяется точками (“.”).
- Заголовок (header) содержит информацию о криптографическом алгоритме, используемом для подписи токена.
- Полезная нагрузка (payload) содержит данные о пользователе и дополнительные метаданные (claims).
- Подпись (signature), вычисляемая по header и payload с секретным ключом, гарантирует целостность и подлинность токена.
Ниже мы реализуем пример на Node.js с Express и MongoDB и покажем, как применить JWT в практической аутентификации.
Быстрый план реализации
- Создать Express сервер.
- Подключить MongoDB через Mongoose.
- Реализовать модель пользователя и контроллеры для регистрации и входа.
- Генерировать JWT при успешном входе и сохранять его в cookie с httpOnly.
- Написать middleware для валидации токена и защитить маршруты.
- Добавить рекомендации по усилению безопасности и тест-кейсы.
Подготовка Express-приложения и базы MongoDB
Мы создадим простой REST API с регистрацией и входом. После успешного входа пользователь сможет делать запросы к защищённым маршрутам.
Код проекта доступен в репозитории (ссылка в описании проекта).
Установите зависимости:
npm install cors dotenv bycrpt mongoose cookie-parser crypto jsonwebtoken mongodbПримечание: в списке пакетов есть опечатка “bycrpt” (должно быть bcrypt). Используйте корректное имя пакета: bcrypt.
Создайте базу данных MongoDB или кластер в облаке и скопируйте строку подключения в файл .env в корне проекта:
CONNECTION_STRING="connection string"Настройка подключения к базе данных
Создайте файл utils/db.js и добавьте код для подключения через Mongoose:
const mongoose = require('mongoose');
const connectDB = async () => {
try {
await mongoose.connect(process.env.CONNECTION_STRING);
console.log("Connected to MongoDB!");
} catch (error) {
console.error("Error connecting to MongoDB:", error);
}
};
module.exports = connectDB;Важно: убедитесь, что в файле .env безопасно хранится строка подключения и что сам файл не попадает в систему контроля версий.
Определение модели данных
Создайте файл model/user.model.js с простой схемой пользователя:
const mongoose = require('mongoose');
const userSchema = new mongoose.Schema({
username: String,
password: {
type: String,
required: true,
unique: true,
},
});
const User = mongoose.model("User", userSchema);
module.exports = User;Примечание: в примерах далее путь импорта контроллеров использует папку “models” (множественное число). Убедитесь, что структура папок в проекте соответствует используемым путям require.
Контроллеры для маршрутов API
Контроллеры отвечают за регистрацию, вход и доступ к защищённым данным. Создайте файл controllers/userControllers.js и добавьте следующий код:
- Контроллер регистрации пользователя:
const User = require('../models/user.model');
const bcrypt = require('bcrypt');
const { generateToken } = require('../middleware/auth');
exports.registerUser = async (req, res) => {
const { username, password } = req.body;
try {
const hash = await bcrypt.hash(password, 10);
await User.create({ username, password: hash });
res.status(201).send({ message: 'User registered successfully' });
} catch (error) {
console.log(error);
res.status(500).send({ message: 'An error occurred!! ' });
}
};Этот фрагмент хеширует пароль с помощью bcrypt и сохраняет запись пользователя в базе.
- Контроллер входа (login):
exports.loginUser = async (req, res) => {
const { username, password } = req.body;
try {
const user = await User.findOne({ username });
if (!user) {
return res.status(404).send({ message: 'User not found' });
}
const passwordMatch = await bcrypt.compare(password, user.password);
if (!passwordMatch) {
return res.status(401).send({ message: 'Invalid login credentials' });
}
const payload = { userId: user._id };
const token = generateToken(payload);
res.cookie('token', token, { httpOnly: true });
res.status(200).json({ message: 'Login successful'});
} catch (error) {
console.log(error);
res.status(500).send({ message: 'An error occurred while logging in' });
}
};При успешной проверке создаётся JWT и сохраняется в cookie с флагом httpOnly — это защищает токен от доступа через JavaScript в браузере.
- Пример защищённого маршрута, возвращающего список пользователей:
exports.getUsers = async (req, res) => {
try {
const users = await User.find({});
res.json(users);
} catch (error) {
console.log(error);
res.status(500).send({ message: 'An error occurred!!' });
}
}; Если JWT хранится в cookie, последующие запросы автоматически будут содержать токен, и сервер сможет его валидировать.
Middleware для аутентификации
Создайте папку middleware и два файла: config.js и auth.js.
Добавьте в config.js:
const crypto = require('crypto');
module.exports = {
secretKey: crypto.randomBytes(32).toString('hex')
};Важно: в этом примере секретный ключ генерируется при каждом запуске. Для реального приложения используйте постоянный секрет (из env-переменной) и обеспечьте безопасное хранение (секреты не должны меняться при каждом деплое, иначе все токены станут недействительны).
Добавьте в auth.js middleware для генерации и валидации токенов:
const jwt = require('jsonwebtoken');
const { secretKey } = require('./config');
const generateToken = (payload) => {
const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
return token ;
};
const verifyToken = (req, res, next) => {
const token = req.cookies.token;
if (!token) {
return res.status(401).json({ message: 'No token provided' });
}
jwt.verify(token, secretKey, (err, decoded) => {
if (err) {
return res.status(401).json({ message: 'Invalid token' });
}
req.userId = decoded.userId;
next();
});
};
module.exports = { generateToken, verifyToken };generateToken подписывает полезную нагрузку секретом и задаёт время жизни (expiresIn). verifyToken извлекает токен из cookie и проверяет его валидность.
Маршруты API
Создайте routes/userRoutes.js и добавьте маршруты:
const express = require('express');
const router = express.Router();
const userControllers = require('../controllers/userControllers');
const { verifyToken } = require('../middleware/auth');
router.post('/api/register', userControllers.registerUser);
router.post('/api/login', userControllers.loginUser);
router.get('/api/users', verifyToken, userControllers.getUsers);
module.exports = router;Точка входа сервера
Обновите server.js:
const express = require('express');
const cors = require('cors');
const app = express();
const port = 5000;
require('dotenv').config();
const connectDB = require('./utils/db');
const cookieParser = require('cookie-parser');
connectDB();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use(cookieParser());
const userRoutes = require('./routes/userRoutes');
app.use('/', userRoutes);
app.listen(port, () => {
console.log(`Server is listening at http://localhost:${port}`);
});Запустите сервер и делайте запросы к маршрутам:
node server.jsУсиление безопасности Node.js REST API
Использование JWT — важная часть аутентификации, но защита API должна быть многоуровневой. Вот практические рекомендации и меры безопасности.
Важно: Ниже — качественные рекомендации. Не используйте сгенерированные при каждом запуске ключи в production.
Рекомендации по безопасности (Security hardening)
- Храните секреты в безопасном хранилище (env, Vault, секреты платформы). Не генерируйте секрет при запуске.
- Используйте HTTPS везде, где передаются токены и учетные данные.
- Устанавливайте cookie-флаги: HttpOnly, Secure и SameSite (например, SameSite=Strict или Lax в зависимости от сценария).
- Ограничивайте срок жизни токена (короткий access token + refresh token с безопасным хранением).
- Реализуйте механизм отзыва токенов (черные списки или хранение версии токена в БД/redis).
- Валидируйте вводимые данные и применяйте защиту от инъекций (включая NoSQL-инъекции для MongoDB).
- Ограничьте число попыток входа (rate limiting) и применяйте брутфорс-защиту.
- Логируйте аутентификационные события и реагируйте на подозрительную активность.
- Проводите регулярный аудит зависимостей и обновления библиотек.
Пример настроек cookie в ответе:
- secure: true (только HTTPS)
- httpOnly: true (недоступно из JS)
- sameSite: ‘Lax’ или ‘Strict’
Когда JWT может не подойти (Counterexamples / когда это не работает)
- Если требуется мгновенный отзыв токена для большого числа пользователей (без центрального хранилища), использование только подписанных JWT может быть неудобно.
- Для очень чувствительных трансакций лучше применять дополнительные механизмы подтверждения (MFA, запрос повторной аутентификации).
- Когда браузерные клиенты работают в условиях строгой политики CORS и сторонних cookie — может понадобиться хранение токена в другом месте или применение протоколов как OAuth2/OpenID Connect.
Альтернативные подходы
- Сессионные куки на сервере (сервер хранит сессии в Redis или БД) — удобный способ отзыва сессий.
- OAuth2 / OpenID Connect — для делегированной аутентификации и интеграции с внешними провайдерами.
- Signed cookies без долгоживущих JWT, если требуется минимальная логика авторизации.
Модель мышления (Mental model)
Думайте о JWT как о подписанном утверждении: сервер подписывает утверждение о пользователе — клиент предъявляет его при запросе. Если подпись валидна и токен не просрочен — запрос можно авторизовать.
Практические чек-листы по ролям
Разделены по роли для ясности и удобства внедрения.
Разработчик:
- Хеширование паролей (bcrypt) перед сохранением.
- Генерация токенов с коротким сроком жизни.
- Использование httpOnly и Secure cookie.
- Валидация входных данных на сервере.
DevOps / Инфраструктура:
- Управление секретами через защищённое хранилище.
- Включение HTTPS/TLS и обновление сертификатов.
- Мониторинг и логирование аутентификационных событий.
Инженер по безопасности:
- Проведение ревью сторонних пакетов и зависимостей.
- Настройка WAF, rate limiting, и IDS/IPS.
- План реагирования на утечку секретов и инциденты.
Мини-методология внедрения (шаги)
- Проектирование: выбрать стратегию токенов (access + refresh или только access) и место хранения (cookie / localStorage / Authorization header).
- Разработка: реализовать регистрацию и вход, генерацию JWT, middleware для проверки.
- Тестирование: написать тест-кейсы для всех сценариев (см. далее).
- Деплой: безопасно настроить переменные окружения, HTTPS и секреты.
- Наблюдение: логирование, мониторинг, ревью безопасности.
Тест-кейсы и критерии приёмки
Критерии приёмки:
- Регистрация создает пользователя с захешированным паролем.
- Вход возвращает httpOnly cookie с JWT и статус 200.
- Доступ к /api/users без токена возвращает 401.
- Доступ к /api/users с корректным токеном возвращает 200 и список пользователей.
- Токен с истёкшим временем жизни вызывает 401.
Примеры тест-кейсов:
- Успешная регистрация: POST /api/register с валидными данными → 201.
- Попытка регистрации с уже существующим username → 500/ошибка уникальности.
- Успешный вход: POST /api/login → cookie token, 200.
- Доступ к защищённому маршруту с подделанным токеном → 401.
Проблемы и способы их решения (Edge cases)
- Потеря секретного ключа / ротация ключей: реализуйте стратегию ротации ключей с переходным периодом или храните версию ключа в payload и поддерживайте карту старых ключей.
- XSS и кража токена: храните токен в httpOnly cookie, используйте Content Security Policy и валидацию вводимых данных.
- CSRF: если вы используете cookie для аутентификации, применяйте защиту от CSRF (CSRF-токены или SameSite cookie).
Глоссарий в одну строчку
- JWT: подписанный JSON-объект для передачи утверждений между сторонами.
- Access token: краткоживущий токен для доступа к ресурсам.
- Refresh token: долгоживущий токен, используемый для получения новых access token.
- HttpOnly cookie: cookie, недоступная для JavaScript в браузере.
Короткое объявление (100–200 слов)
Реализована простая и безопасная аутентификация для Node.js REST API с использованием JWT, Express и MongoDB. Пользователи могут зарегистрироваться и войти, после чего сервер генерирует JWT и помещает его в httpOnly cookie. Middleware проверяет токен для доступа к защищённым маршрутам. В статье приведены примеры кода для контроллеров, middleware и маршрутов, а также рекомендации по усилению безопасности: хранение секретов, HTTPS, короткий срок жизни токенов, настройка флагов cookie и отзыва токенов. Включены чек-листы для разработчиков, DevOps и инженеров по безопасности, тест-кейсы и возможные сценарии отказа. Это руководство поможет быстро внедрить базовую, но надёжную схему аутентификации в вашем проекте.
Краткое резюме
Важно: сочетание JWT с другими мерами безопасности обеспечивает надёжную защиту API. Ниже — ключевые выводы.
- JWT упрощает аутентификацию, но не заменяет комплексный подход к безопасности.
- Хранение токена в httpOnly cookie снижает риск кражи через XSS, но требует защиты от CSRF.
- Старайтесь использовать короткий срок жизни access token и безопасный механизм refresh.
- Храните секреты централизованно и не генерируйте их при каждом запуске.
Примечание: приведённый код — учебный пример. Для продакшена внесите дополнительные меры: надёжное управление секретами, аудит зависимостей, мониторинг и тестирование.
Похожие материалы
Установка Apache Tomcat 10 на Ubuntu 20.04
Отключить иллюстрации в строке поиска Windows
Как сообщить о посте или профиле в Facebook
SMTP в Python: отправка писем через Gmail
Wi‑Fi точка на Linux с linux-wifi-hotspot