Как бы вы реализовали систему авторизации с тремя ролями: пользователь, менеджер, администратор?

Ответ

В моих проектах на Node.js я обычно строю такую систему на основе JWT (JSON Web Tokens) для аутентификации и middleware для авторизации по ролям. Вот примерный стек и реализация:

1. Структура данных (на примере Mongoose):

const userSchema = new mongoose.Schema({
  email: { type: String, unique: true, required: true },
  passwordHash: { type: String, required: true },
  role: {
    type: String,
    enum: ['user', 'manager', 'admin'],
    default: 'user'
  }
});

2. Middleware для проверки аутентификации и роли:

// authMiddleware.js
const jwt = require('jsonwebtoken');

const authenticate = (req, res, next) => {
  const token = req.header('Authorization')?.replace('Bearer ', '');
  if (!token) return res.status(401).json({ error: 'Access denied' });

  try {
    const verified = jwt.verify(token, process.env.JWT_SECRET);
    req.user = verified; // В токене содержится { userId, role }
    next();
  } catch (err) {
    res.status(400).json({ error: 'Invalid token' });
  }
};

const authorize = (...allowedRoles) => {
  return (req, res, next) => {
    if (!req.user || !allowedRoles.includes(req.user.role)) {
      return res.status(403).json({ error: 'Forbidden: insufficient privileges' });
    }
    next();
  };
};

module.exports = { authenticate, authorize };

3. Применение в роутах Express:

// routes/admin.js
const { authenticate, authorize } = require('../middleware/authMiddleware');
const router = require('express').Router();

// Только админ
router.get('/stats', authenticate, authorize('admin'), getStats);

// Админ и менеджер
router.put('/users/:id', authenticate, authorize('admin', 'manager'), updateUser);

// Любой аутентифицированный пользователь
router.get('/profile', authenticate, (req, res) => {
  res.json(req.user);
});

Ключевые моменты:

  • Роль пользователя хранится в БД и включается в JWT-токен при логине.
  • Middleware authenticate проверяет валидность токена и добавляет данные пользователя в req.user.
  • Middleware authorize проверяет, есть ли у пользователя одна из разрешенных ролей. Админ ('admin') по умолчанию имеет доступ ко всем эндпоинтам.
  • Для сложных политик доступа (например, «менеджер может редактировать только своих подчиненных») внутри контроллера добавляется дополнительная бизнес-логика.

Ответ 18+ 🔞

А, ну это ж классика, ебать мои старые костыли! Каждый второй проект на Node.js через это проходит. Слушай, как я обычно эту карусель кручу.

В общем, берёшь JWT – это как пропуск в закрытый клуб, только вместо штампа – куча непонятных букв и цифр. И middleware – такие швейцары, которые проверяют, можно ли тебе пройти дальше или пора на выход.

1. Для начала, как данные хранить (на примере Mongoose):

const userSchema = new mongoose.Schema({
  email: { type: String, unique: true, required: true },
  passwordHash: { type: String, required: true },
  role: {
    type: String,
    enum: ['user', 'manager', 'admin'],
    default: 'user'
  }
});

Всё просто: почта, хеш пароля (никогда, слышишь, НИКОГДА пароль в открытую не пили!) и роль. Роли – как в театре: 'user' (зритель), 'manager' (осветитель) и 'admin' (режиссёр, которому всё можно).

2. Дальше – мозги системы, middleware:

// authMiddleware.js
const jwt = require('jsonwebtoken');

const authenticate = (req, res, next) => {
  const token = req.header('Authorization')?.replace('Bearer ', '');
  if (!token) return res.status(401).json({ error: 'Access denied' });

  try {
    const verified = jwt.verify(token, process.env.JWT_SECRET);
    req.user = verified; // В токене теперь { userId, role } – вся подноготная
    next();
  } catch (err) {
    res.status(400).json({ error: 'Invalid token' });
  }
};

const authorize = (...allowedRoles) => {
  return (req, res, next) => {
    if (!req.user || !allowedRoles.includes(req.user.role)) {
      return res.status(403).json({ error: 'Forbidden: insufficient privileges' });
    }
    next();
  };
};

module.exports = { authenticate, authorize };

authenticate – это как охранник на входе: "Предъяви пропуск!" Нет токена или он кривой – иди нахуй, 401 ошибка. authorize – уже второй охранник внутри: "А тебе в этот кабинет можно?" Проверяет роль. Не в списке – получи 403, свободен.

3. Ну и как это всё прикрутить к роутам:

// routes/admin.js
const { authenticate, authorize } = require('../middleware/authMiddleware');
const router = require('express').Router();

// Сюда только админ пролезет
router.get('/stats', authenticate, authorize('admin'), getStats);

// Сюда админ или менеджер
router.put('/users/:id', authenticate, authorize('admin', 'manager'), updateUser);

// А сюда любой, кто авторизовался
router.get('/profile', authenticate, (req, res) => {
  res.json(req.user);
});

Суть в чём, ёпта:

  • Роль юзера в базе лежит и в JWT-токен при логине зашивается.
  • authenticate проверяет, не хуй в пальто ли этот токен, и если всё ок – вешает данные на req.user.
  • authorize смотрит: а твоя роль в списке приглашённых? Админ ('admin') – по умолчанию царь и бог, ему везде зелёный свет.
  • Если логика сложнее (типа "менеджер может только своих подопечных трогать") – это уже внутри самого контроллера дописываешь, обычными if-ами. Middleware – только дверь открывает, а что внутри делать – твои проблемы.

Вот и вся магия. Ничего космического, но работает как швейцарские часы, если, конечно, секретный ключ (JWT_SECRET) не закоммитишь в публичный репозиторий, а то будет тебе хиросима и нигерсраки на ровном месте.