Как реализовать аутентификацию по JWT в Node.js + Express

7 минут чтения
Средний рейтинг статьи — 4.7

JWT — это компактный способ передавать данные об идентификации пользователя между клиентом и сервером в виде зашифрованной (подписанной) строки.
Преимущества:

  • Без состояния (stateless) — серверу не нужно хранить сессии в памяти или БД.
  • Универсальность — один и тот же токен можно использовать между разными сервисами и языками.
  • Гибкость хранения — клиент может хранить токен в localStorage, sessionStorage или куках.

Типичный поток работы:

  1. Пользователь отправляет логин/пароль на сервер.
  2. Сервер проверяет данные и генерирует JWT с полезной нагрузкой (например, userId).
  3. Клиент хранит токен и отправляет его в заголовке Authorization: Bearer <token> при каждом запросе.
  4. Сервер на каждом запросе валидирует подпись и срок действия токена.

Установка и подготовка проекта

Создаём папку проекта и устанавливаем зависимости:

mkdir jwt-auth-example && cd jwt-auth-example
npm init -y
npm install express jsonwebtoken bcryptjs dotenv
npm install --save-dev nodemon
  • express – HTTP-фреймворк.
  • jsonwebtoken – создание и проверка JWT.
  • bcryptjs – хеширование паролей.
  • dotenv – хранение секретов в .env.

В package.json добавим скрипт:

"scripts": {
  "dev": "nodemon index.js"
}

Структура проекта

jwt-auth-example/
├─ index.js
├─ .env
└─ users.js  # простая «база» пользователей

Файл .env:

JWT_SECRET=supersecretkey
JWT_EXPIRES=1h

Простейшая «база данных»

Для наглядности и чтобы избежать лишнего усложнения создадим простую мок-базу users.

users.js:

const bcrypt = require('bcryptjs');
 
const users = []; // временное хранилище
 
async function createUser(username, password) {
  const hashed = await bcrypt.hash(password, 10);
  users.push({ username, password: hashed });
}
 
async function findUser(username) {
  return users.find(u => u.username === username);
}
 
module.exports = { createUser, findUser };

Сервер Express с регистрацией и логином

index.js:

require('dotenv').config();
const express = require('express');
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
const { createUser, findUser } = require('./users');
 
const app = express();
app.use(express.json());
 
// Регистрация
app.post('/register', async (req, res) => {
  const { username, password } = req.body;
  if (await findUser(username))
    return res.status(400).json({ message: 'User already exists' });
 
  await createUser(username, password);
  res.json({ message: 'User registered' });
});
 
// Логин
app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  const user = await findUser(username);
  if (!user) return res.status(401).json({ message: 'Invalid credentials' });
 
  const match = await bcrypt.compare(password, user.password);
  if (!match) return res.status(401).json({ message: 'Invalid credentials' });
 
  const token = jwt.sign(
    { username },
    process.env.JWT_SECRET,
    { expiresIn: process.env.JWT_EXPIRES }
  );
 
  res.json({ token });
});
 
// Middleware проверки токена
function authMiddleware(req, res, next) {
  const authHeader = req.headers['authorization'];
  const token = authHeader && authHeader.split(' ')[1];
  if (!token) return res.sendStatus(401);
 
  jwt.verify(token, process.env.JWT_SECRET, (err, user) => {
    if (err) return res.sendStatus(403);
    req.user = user; // в user лежит payload токена
    next();
  });
}
 
// Пример защищённого маршрута
app.get('/profile', authMiddleware, (req, res) => {
  res.json({ message: `Hello, ${req.user.username}!` });
});
 
app.listen(3000, () => console.log('Server running on http://localhost:3000'));

Проверяем

  1. Регистрация:
    POST http://localhost:3000/register
    JSON: {"username":"alice","password":"secret"}
  2. Логин:
    POST http://localhost:3000/login
    → получаем токен:
{"token":"eyJhbGciOi..."}
  1. Защищённый маршрут:
    GET http://localhost:3000/profile
    Заголовок: Authorization: Bearer <token>
    Ответ:
{"message":"Hello, alice!"}

Практические советы

  • Хранение токена:
    – В SPA обычно localStorage или sessionStorage.
    – Для защиты от XSS можно использовать httpOnly-куки.

  • Обновление токена (refresh token):
    В продакшене часто используют пару: короткоживущий access-token + долгоживущий refresh-token.

  • Отзыв токена:
    JWT по умолчанию нельзя «отозвать». Если нужно — храните список заблокированных токенов или используйте версионирование токенов в базе.

  • HTTPS:
    Всегда передавайте токены по защищённому соединению.

Вывод

JWT позволяет реализовать аутентификацию без сохранения сессий на сервере. В связке с Node.js и Express достаточно нескольких библиотек, чтобы развернуть рабочий прототип: хэшируем пароли, генерируем и проверяем токены, защищаем маршруты через middleware. Для реального продакшена добавьте хранение пользователей в БД, refresh-токены и обязательно используйте HTTPS.

7 минут чтения
Средний рейтинг статьи — 4.7

Настроить мониторинг за 30 секунд

Надежные оповещения о даунтаймах. Без ложных срабатываний