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

Реализация ролевого контроля доступа (RBAC) с Passport.js и JWT

7 min read Security Обновлено 15 Dec 2025
RBAC с Passport.js и JWT — реализация и безопасность
RBAC с Passport.js и JWT — реализация и безопасность

MacBook Pro laptop with program code on screen

Определение: Ролевой контроль доступа (RBAC) — модель управления доступом, в которой разрешения ассоциированы с ролями, а пользователи получают роли.

Важно: RBAC — это про авторизацию (кто что может делать), а не про аутентификацию (кто вы есть).

Зачем использовать RBAC

  • Централизованный контроль прав. Администратор назначает роли — система применяет права.
  • Простота управления в больших командах и системах.
  • Чёткое разделение обязанностей для соответствия требованиям безопасности.

Примеры ролей: admin, editor, user, guest.

Подходы к реализации RBAC

Основные варианты:

  • Использовать специализированные библиотеки RBAC (например, готовые ACL/RBAC модули). Это удобно при сложных правилах и иерархиях.
  • Интегрировать RBAC в существующую систему аутентификации (Passport.js + JWT). Хорошо подходит для простых и средних по сложности приложений.

В этой статье показан второй вариант: хранение роли в JWT и проверка роли в middleware.

An open laptop with a code editor showing on screen

ALT изображения: Открытый ноутбук с редактором кода на экране

Архитектурная идея в одном предложении

При логине пользователю присваивается JWT с полем role. Для защищённых маршрутов middleware проверяет токен и сравнивает роль с требуемой.

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

  1. Инициализируйте проект Node.js и установите зависимости:
npm install express cors dotenv mongoose cookie-parser jsonwebtoken mongodb passport passport-local
  1. Создайте файл .env в корне проекта и добавьте строки:
CONNECTION_URI="connection URI"
SECRET_KEY="This is a sample secret key."

Замените CONNECTION_URI и SECRET_KEY на реальные значения перед деплоем.

Шаг 2 — Подключение к MongoDB

Создайте файл utils/db.js и добавьте код для подключения через Mongoose:

const mongoose = require('mongoose');

const connectDB = async () => {
  try {
    await mongoose.connect(process.env.CONNECTION_URI);
    console.log("Connected to MongoDB!");
  } catch (error) {
    console.error("Error connecting to MongoDB:", error);
  }
};

module.exports = connectDB;

Шаг 3 — Модель пользователя

Создайте models/user.model.js с простой схемой:

const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  username: String,
  password: String,
  role: String
});

module.exports = mongoose.model('User', userSchema);

Примечание: Для продакшн-решений пароли нужно хранить в виде хэшей (bcrypt) и добавлять валидацию.

Шаг 4 — Контроллеры пользователей

Создайте controllers/user.controller.js и добавьте логику регистрации, логина и получения списка пользователей:

const User = require('../models/user.model');
const passport = require('passport');
const { generateToken } = require('../middleware/auth');
require('../middleware/passport')(passport);

exports.registerUser = async (req, res) => {
  const { username, password, role } = req.body;

  try {
    await User.create({ username, password, role });
    res.status(201).json({ message: 'User registered successfully' });
  } catch (error) {
    console.log(error);
    res.status(500).json({ message: 'An error occurred!' });
  }
};

exports.loginUser = (req, res, next) => {
  passport.authenticate('local', { session: false }, (err, user, info) => {
    if (err) {
      console.log(err);
      return res.status(500).json({
        message: 'An error occurred while logging in'
      });
    }

    if (!user) {
      return res.status(401).json({
        message: 'Invalid login credentials'
      });
    }

    req.login(user, { session: false }, (err) => {
      if (err) {
        console.log(err);
        return res.status(500).json({
          message: 'An error occurred while logging in'
        });
      }

      const { _id, username, role } = user;
      const payload = { userId: _id, username, role };
      const token = generateToken(payload);
      res.cookie('token', token, { httpOnly: true });
      return res.status(200).json({ message: 'Login successful' });
    });
  })(req, res, next);
};

exports.getUsers = async (req, res) => {
  try {
    const users = await User.find({});
    res.json(users);
  } catch (error) {
    console.log(error);
    res.status(500).json({ message: 'An error occurred!' });
  }
};

Пояснения:

  • registerUser: создаёт запись пользователя с полем role.
  • loginUser: аутентифицирует через Passport, генерирует JWT с ролью и кладёт токен в cookie.
  • getUsers: защищённый маршрут для демонстрации проверки роли.

Шаг 5 — Passport.js локальная стратегия

Создайте middleware/passport.js:

const LocalStrategy = require('passport-local').Strategy;
const User = require('../models/user.model');

module.exports = (passport) => {
  passport.use(
    new LocalStrategy(async (username, password, done) => {
      try {
        const user = await User.findOne({ username });

        if (!user) {
          return done(null, false);
        }

        if (user.password !== password) {
          return done(null, false);
        }

        return done(null, user);
      } catch (error) {
        return done(error);
      }
    })
  );
};

Важно: В реальном приложении сравнивайте хэши паролей (bcrypt) вместо прямого сравнения.

Шаг 6 — Middleware для JWT: генерация и верификация

Создайте middleware/auth.js:

const jwt = require('jsonwebtoken');
const secretKey = process.env.SECRET_KEY;

const generateToken = (payload) => {
  const token = jwt.sign(payload, secretKey, { expiresIn: '1h' });
  return token;
};

const verifyToken = (requiredRole) => (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;

    if (decoded.role !== requiredRole) {
      return res.status(403).json({
        message: 'You do not have the authorization and permissions to access this resource.'
      });
    }

    next();
  });
};

module.exports = { generateToken, verifyToken };

Как это работает: middleware читает cookie token, верифицирует подпись и проверяет поле role.

Шаг 7 — Маршруты API

Создайте routes/userRoutes.js:

const express = require('express');
const router = express.Router();
const userControllers = require('../controllers/user.controller');
const { verifyToken } = require('../middleware/auth');

router.post('/api/register', userControllers.registerUser);
router.post('/api/login', userControllers.loginUser);

router.get('/api/users', verifyToken('admin'), userControllers.getUsers);

module.exports = router;

Пояснение: маршрут /api/users доступен только пользователям с ролью admin.

Шаг 8 — Основной файл сервера

Обновите server.js:

const express = require('express');
const cors = require('cors');
const cookieParser = require('cookie-parser');
const app = express();
const port = 5000;
require('dotenv').config();
const connectDB = require('./utils/db');
const passport = require('passport');
require('./middleware/passport')(passport);

connectDB();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
app.use(cookieParser());
app.use(passport.initialize());

const userRoutes = require('./routes/userRoutes');
app.use('/', userRoutes);

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

Запустите сервер:

node server.js

Тестирование и критерии приёмки

Краткий набор тестов для проверки RBAC:

  • Регистрация: POST /api/register — возвращает 201 и создаёт пользователя с ролью.
  • Логин: POST /api/login — выдаёт cookie token и код 200 при корректных данных.
  • Доступ к /api/users:
    • с ролью admin — возвращает список пользователей (200);
    • с ролью user или без токена — возвращает 403 или 401.

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

  • Пароли хранятся как хэши (в реальном проекте).
  • Токен имеет срок жизни (например, 1 час).
  • Роль пользователя присутствует в полезной нагрузке (payload) токена.

Рекомендации по безопасности и харднинг

  • Хэшируйте пароли с bcrypt (или Argon2).
  • Используйте короткий срок жизни access-токенов и refresh-токены с безопасным хранением.
  • Подписывайте JWT надёжным SECRET_KEY и используйте окружение для секретов.
  • Защитите cookie: httpOnly, secure (в HTTPS), sameSite по необходимости.
  • Логи: не записывайте секреты, не логируйте токены.
  • Валидация входных данных: строго валидируйте тело запроса и параметры.
  • Применяйте rate limiting на маршруты логина.

Важно: RBAC с JWT использует утверждения (claims) внутри токена. Если роль меняется в базе, токен с прежней ролью остаётся действительным до истечения — учитывайте это при проектировании.

Модели зрелости RBAC (простая шкала)

  • Уровень 0 — Отсутствие контроля (все доступы открыты).
  • Уровень 1 — Жёсткие роли (admin/user) в приложении, проверка на уровне маршрутов.
  • Уровень 2 — Разделение прав и группировка ролей; хранение ролей в БД; централизованные политики.
  • Уровень 3 — Динамические правила, временные права, аудит и управление правами через UI.

Когда RBAC не подходит / альтернативы

  • Сильно динамичные права на ресурсах (например, разрешения зависят от атрибутов объекта) — рассмотрите ABAC (attribute-based access control).
  • Сложные иерархии/наследование правил — используйте специализированные RBAC/ACL библиотеки.

Пошаговый чеклист для внедрения RBAC (роль: разработчик)

  • Добавить поле role в модель пользователя.
  • Добавить хранение роли при регистрации.
  • Генерировать JWT с полем role при логине.
  • Создать middleware verifyToken(requiredRole).
  • Защитить маршруты, указав требуемую роль.
  • Реализовать хеширование паролей.
  • Настроить безопасные cookie.
  • Добавить тесты доступа и интеграционные тесты.

Шаблон сценариев тестирования (минимум)

  1. Регистрация нового admin-пользователя и проверка роли.
  2. Регистрация пользователя без роли — проверить поведение (валидация).
  3. Попытка доступа к /api/users без токена — должен быть 401.
  4. Попытка доступа с токеном role=user — должен быть 403.
  5. Попытка доступа с токеном role=admin — должен быть 200.

Мини-методология внедрения в проект

  1. Подготовьте миграцию модели (добавьте role).
  2. Внедрите хеширование паролей.
  3. Добавьте генерацию JWT с role.
  4. Внедрите middleware и защитите ключевые маршруты.
  5. Напишите тесты и проведите ревью безопасности.
  6. Разверните на staging и выполните контроль доступа с разными ролями.

Советы по миграции (legacy → RBAC)

  • Сначала внедрите чтение роли, не выбрасывая старые проверки.
  • Обновите админ-панель для назначения ролей пользователям.
  • Запустите миграцию: по умолчанию назначьте роль user существующим пользователям.
  • Обновите документацию API.

Decision flow: проверка доступа (Mermaid)

flowchart TD
  A[Запрос к защищённому маршруту] --> B{Есть cookie token?}
  B -- Нет --> C[Ответ 401: No token provided]
  B -- Да --> D[Верификация подписи JWT]
  D --> E{Токен валиден?}
  E -- Нет --> F[Ответ 401: Invalid token]
  E -- Да --> G[Проверка поля role в токене]
  G --> H{role === requiredRole?}
  H -- Нет --> I[Ответ 403: Нет прав]
  H -- Да --> J[next'' — Доступ открыт]

Примеры альтернативных реализаций

  • Хранить права отдельно от ролей и проверять соответствие правил на уровне ресурсов.
  • Использовать готовые RBAC/ACL библиотеки для управления сложными правами (например, Casbin для гибкой политики).

Частые ошибки и как их избежать

  • Хранение паролей в открытом виде — всегда хэшируйте.
  • Полагаться только на роль в токене при долгом сроке жизни — используйте короткий срок действия и механизм отзыва токенов.
  • Не проверять входные данные — это приводит к уязвимостям.

Локальные особенности для развёртывания (RU/EU)

  • Храните секреты в управляемом хранилище (Vault, AWS Secrets Manager) и не в git.
  • При обработке персональных данных соблюдайте локальное законодательство (например, GDPR в ЕС).

Факто-бокс: ключевые точки

  • JWT: удобен для распределённых систем, но токены трудно отзыввать.
  • Роль в payload: быстро и просто, но нужно учитывать обновление ролей.
  • Безопасность: httpOnly + secure cookies, хэширование паролей, короткий срок действия токенов.

Роль-based чеклист для администратора

  • Проверить, что у новых пользователей назначена корректная роль.
  • Отозвать доступ у уволенных сотрудников и изменить их роли.
  • Проверить логи доступа для подозрительных входов.

Короткий глоссарий

  • JWT — JSON Web Token: компактный токен для передачи утверждений.
  • RBAC — Role-Based Access Control: модель управления доступом по ролям.
  • Passport.js — middleware для аутентификации в Node.js.

Часто задаваемые вопросы

Можно ли хранить роль в базе и не класть её в JWT?

Да. Тогда при каждом запросе требуются дополнительные обращения к базе для проверки роли. Это безопаснее в плане отзывов прав, но дороже по производительности.

Как отозвать JWT до истечения срока?

  • Используйте короткие сроки жизни access-токенов и механизм refresh-токенов.
  • Храните чёрный список отозванных токенов или храните версию токена в базе (tokenVersion) и сравнивайте.

Куда смотреть дальше

  • Добавьте поддержку ролей с иерархией и правами на уровне ресурсов.
  • Реализуйте аудит эффективности (кто что менял и когда).

Итог: RBAC с Passport.js и JWT — практичное решение для многих приложений. Оно простое в реализации и даёт ясную модель управления доступом. При необходимости масштабируйте модель до ABAC или подключайте специализированные движки политик.

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

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

Как найти приложения для Android Wear 2.0
Android.

Как найти приложения для Android Wear 2.0

Как размыть фон в Canva: 3 простых способа
Дизайн

Как размыть фон в Canva: 3 простых способа

Установить и протестировать Windows 10 S
Windows

Установить и протестировать Windows 10 S

RRoD на Xbox 360: как устранить и диагностировать
Техподдержка

RRoD на Xbox 360: как устранить и диагностировать

Как проверить версию Ubuntu
Linux

Как проверить версию Ubuntu

Как сделать копию документа Word
Работа с документами

Как сделать копию документа Word