Гид по технологиям

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

8 min read Безопасность Обновлено 10 Apr 2026
JWT авторизация в Node.js: Express + MongoDB
JWT авторизация в Node.js: Express + MongoDB

Кратко

Ноутбук с кодом на столе в уютном кафе, рядом виден зелёный комнатный растение

Когда вы создаёте приложение, важно защитить конфиденциальные данные от неавторизованного доступа. Многие современные веб-, мобильные и облачные приложения используют REST API как основной канал взаимодействия. Поэтому при проектировании и разработке серверных API безопасность должна быть приоритетом.

Один из распространённых и практичных способов защиты REST API — использование JSON Web Token (JWT). JWT обеспечивает удобный механизм аутентификации и авторизации, помогающий ограничивать доступ к защищённым ресурсам.

Что такое JSON Web Tokens

JSON Web Token (JWT) — это стандарт передачи утверждений (claims) в компактном и самодостаточном виде. JWT позволяет безопасно передавать информацию между клиентом и сервером.

REST API может использовать JWT для идентификации и аутентификации пользователя при отправке HTTP-запросов к защищённым маршрутам.

Пример кодированного JWT слева и декодированной структуры токена с тремя частями справа

JSON Web Token состоит из трёх частей: header (заголовок), payload (полезная нагрузка) и signature (подпись). Каждая часть кодируется и объединяется точками (“.”).

  • Заголовок (header) содержит информацию о криптографическом алгоритме, используемом для подписи токена.
  • Полезная нагрузка (payload) содержит данные о пользователе и дополнительные метаданные (claims).
  • Подпись (signature), вычисляемая по header и payload с секретным ключом, гарантирует целостность и подлинность токена.

Ниже мы реализуем пример на Node.js с Express и MongoDB и покажем, как применить JWT в практической аутентификации.

Быстрый план реализации

  1. Создать Express сервер.
  2. Подключить MongoDB через Mongoose.
  3. Реализовать модель пользователя и контроллеры для регистрации и входа.
  4. Генерировать JWT при успешном входе и сохранять его в cookie с httpOnly.
  5. Написать middleware для валидации токена и защитить маршруты.
  6. Добавить рекомендации по усилению безопасности и тест-кейсы.

Подготовка 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 и добавьте следующий код:

  1. Контроллер регистрации пользователя:
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 и сохраняет запись пользователя в базе.

  1. Контроллер входа (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 в браузере.

  1. Пример защищённого маршрута, возвращающего список пользователей:
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.
  • План реагирования на утечку секретов и инциденты.

Мини-методология внедрения (шаги)

  1. Проектирование: выбрать стратегию токенов (access + refresh или только access) и место хранения (cookie / localStorage / Authorization header).
  2. Разработка: реализовать регистрацию и вход, генерацию JWT, middleware для проверки.
  3. Тестирование: написать тест-кейсы для всех сценариев (см. далее).
  4. Деплой: безопасно настроить переменные окружения, HTTPS и секреты.
  5. Наблюдение: логирование, мониторинг, ревью безопасности.

Тест-кейсы и критерии приёмки

Критерии приёмки:

  • Регистрация создает пользователя с захешированным паролем.
  • Вход возвращает 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.
  • Храните секреты централизованно и не генерируйте их при каждом запуске.

Примечание: приведённый код — учебный пример. Для продакшена внесите дополнительные меры: надёжное управление секретами, аудит зависимостей, мониторинг и тестирование.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Установка Apache Tomcat 10 на Ubuntu 20.04
DevOps

Установка Apache Tomcat 10 на Ubuntu 20.04

Отключить иллюстрации в строке поиска Windows
Windows

Отключить иллюстрации в строке поиска Windows

Как сообщить о посте или профиле в Facebook
Социальные сети

Как сообщить о посте или профиле в Facebook

SMTP в Python: отправка писем через Gmail
Python

SMTP в Python: отправка писем через Gmail

Wi‑Fi точка на Linux с linux-wifi-hotspot
Инструкции

Wi‑Fi точка на Linux с linux-wifi-hotspot

Кастомные рингтоны на Android — создать и установить
Android.

Кастомные рингтоны на Android — создать и установить