Авторизация и аутентификация в JavaScript (Node.js)
В современной веб-разработке на Node.js безопасность пользовательских данных и контроль доступа — критически важные задачи. Аутентификация (проверка подлинности) и авторизация (проверка прав) — два столпа любой защищённой системы. В этой статье мы разберём ключевые инструменты и библиотеки для реализации этих механизмов на платформе Node.js.
Основные понятия
Аутентификация — процесс проверки, что пользователь — это тот, за кого себя выдаёт (обычно через логин/пароль, токены или биометрию). Авторизация — определение, какие действия и ресурсы доступны аутентифицированному пользователю (роли, права доступа).
Популярные библиотеки для Node.js
1. Passport.js — универсальная стратегия аутентификации
Passport.js — самая популярная middleware-библиотека для Express. Она поддерживает более 500 стратегий: локальную (логин/пароль), OAuth2 (Google, Facebook, GitHub), JWT и многие другие.
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false, { message: 'Неверное имя пользователя' }); }
if (!user.validPassword(password)) { return done(null, false, { message: 'Неверный пароль' }); }
return done(null, user);
});
}
));
// Сериализация и десериализация пользователя
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
2. JSON Web Tokens (JWT) — безсессионная аутентификация
Библиотека jsonwebtoken позволяет создавать и верифицировать JWT-токены. Это идеально для REST API и SPA-приложений.
const jwt = require('jsonwebtoken');
const secretKey = process.env.JWT_SECRET;
// Создание токена при входе
const token = jwt.sign(
{ userId: user.id, role: user.role },
secretKey,
{ expiresIn: '24h' }
);
// Middleware для проверки токена
function authMiddleware(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1]; // Bearer TOKEN
if (!token) return res.status(401).json({ error: 'Токен не предоставлен' });
jwt.verify(token, secretKey, (err, decoded) => {
if (err) return res.status(403).json({ error: 'Недействительный токен' });
req.user = decoded;
next();
});
}
3. bcrypt — безопасное хэширование паролей
Никогда не храните пароли в открытом виде! bcrypt — золотой стандарт для хэширования с солью.
const bcrypt = require('bcrypt');
const saltRounds = 10;
// Хэширование пароля при регистрации
async function hashPassword(plainPassword) {
const salt = await bcrypt.genSalt(saltRounds);
const hash = await bcrypt.hash(plainPassword, salt);
return hash;
}
// Проверка пароля при входе
async function checkPassword(plainPassword, hash) {
return await bcrypt.compare(plainPassword, hash);
}
4. express-session + connect-redis — управление сессиями
Для традиционных веб-приложений с серверными сессиями используйте express-session в паре с Redis для масштабирования.
const session = require('express-session');
const RedisStore = require('connect-redis')(session);
app.use(session({
store: new RedisStore({ client: redisClient }),
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === 'production', // HTTPS
httpOnly: true,
maxAge: 24 * 60 * 60 * 1000 // 1 день
}
}));
Комплексный пример: регистрация и вход
Соберём всё вместе — простой API на Express с регистрацией, входом и защищённым маршрутом.
const express = require('express');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
const app = express();
app.use(express.json());
// Модель пользователя (упрощённо)
const users = [];
// Регистрация
app.post('/register', async (req, res) => {
try {
const { username, password } = req.body;
const existingUser = users.find(u => u.username === username);
if (existingUser) return res.status(400).json({ error: 'Пользователь уже существует' });
const hashedPassword = await bcrypt.hash(password, 10);
const newUser