← Назад к вопросам
Для чего используется JSON Web Token?
2.0 Middle🔥 241 комментариев
#API и сетевые протоколы#Безопасность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
JSON Web Token (JWT) в Node.js Backend
JWT — стандартный способ передачи информации о пользователе между клиентом и сервером в защищённом виде. Это один из самых распространённых механизмов аутентификации в современных приложениях.
1. Структура JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
[Header].[Payload].[Signature]
Компоненты:
- Header — алгоритм и тип (Base64 encoded JSON)
- Payload — данные пользователя (claims) (Base64 encoded JSON)
- Signature — подпись для верификации
2. Основные случаи использования
Аутентификация (Authentication)
import jwt from 'jsonwebtoken';
// 1. Пользователь логинится
router.post('/login', async (req, res) => {
const { email, password } = req.body;
// Проверяем credentials
const user = await User.findOne({ email });
if (!user || !user.validatePassword(password)) {
return res.status(401).json({ error: 'Invalid credentials' });
}
// Генерируем JWT
const token = jwt.sign(
{
userId: user.id,
email: user.email,
role: user.role
},
process.env.JWT_SECRET,
{ expiresIn: '24h' }
);
res.json({ token });
});
// 2. Клиент отправляет token в header
// Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
// 3. Сервер верифицирует на каждый запрос
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'No token' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
};
Авторизация (Authorization)
// Проверяем роль пользователя в token
const adminOnly = (req, res, next) => {
if (req.user.role !== 'admin') {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
router.delete('/users/:id', authMiddleware, adminOnly, (req, res) => {
// Только админы могут удалять пользователей
});
3. JWT vs Session
JWT:
- Stateless (не нужна БД для проверки)
- Масштабируется (работает для микросервисов)
- Self-contained (вся информация в token)
- Отправляется в каждом запросе
Session:
- Stateful (требует БД/памяти сервера)
- Проще логировать выход
- Меньше данных в запросе
- Более защищено (token не видно клиенту)
4. Реальный пример
import jwt from 'jsonwebtoken';
import bcrypt from 'bcrypt';
interface JWTPayload {
userId: string;
email: string;
role: 'user' | 'admin';
iat?: number;
exp?: number;
}
class AuthService {
private jwtSecret = process.env.JWT_SECRET || 'secret';
private jwtExpiration = '7d';
// Генерация токена
generateToken(user: User): string {
const payload: JWTPayload = {
userId: user.id,
email: user.email,
role: user.role
};
return jwt.sign(payload, this.jwtSecret, {
expiresIn: this.jwtExpiration,
algorithm: 'HS256'
});
}
// Верификация токена
verifyToken(token: string): JWTPayload | null {
try {
const decoded = jwt.verify(token, this.jwtSecret) as JWTPayload;
return decoded;
} catch (error) {
if (error instanceof jwt.TokenExpiredError) {
console.log('Token expired');
} else if (error instanceof jwt.JsonWebTokenError) {
console.log('Invalid token');
}
return null;
}
}
// Логин с паролем
async login(email: string, password: string): Promise<string> {
const user = await User.findOne({ email });
if (!user) {
throw new Error('User not found');
}
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) {
throw new Error('Invalid password');
}
return this.generateToken(user);
}
// Рефреш токена
refreshToken(token: string): string | null {
const payload = this.verifyToken(token);
if (!payload) {
return null; // Token невалиден
}
// Генерируем новый token с теми же данными
return this.generateToken({
id: payload.userId,
email: payload.email,
role: payload.role
} as User);
}
}
5. Рефреш токены (Refresh Tokens)
Для безопасности используют два типа токенов:
interface AuthTokens {
accessToken: string; // Короткоживущий (15 мин)
refreshToken: string; // Долгоживущий (7 дней)
}
class TokenService {
generateTokenPair(user: User): AuthTokens {
// Access token — короткий
const accessToken = jwt.sign(
{ userId: user.id, email: user.email },
process.env.JWT_SECRET,
{ expiresIn: '15m' } // 15 минут
);
// Refresh token — длинный, хранится в БД
const refreshToken = jwt.sign(
{ userId: user.id, type: 'refresh' },
process.env.REFRESH_SECRET,
{ expiresIn: '7d' } // 7 дней
);
// Сохраняем refresh token в БД
db.refreshTokens.create({
userId: user.id,
token: refreshToken,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)
});
return { accessToken, refreshToken };
}
async refreshAccessToken(refreshToken: string): Promise<string> {
// Проверяем в БД
const stored = await db.refreshTokens.findOne({ token: refreshToken });
if (!stored || stored.expiresAt < new Date()) {
throw new Error('Refresh token expired');
}
// Генерируем новый access token
return jwt.sign(
{ userId: stored.userId },
process.env.JWT_SECRET,
{ expiresIn: '15m' }
);
}
}
6. Безопасность JWT
❌ Ошибки:
// Плохо: хранишь пароль в JWT
jwt.sign({ userId: user.id, password: user.password }, secret);
// Плохо: используешь слабый secret
jwt.sign(payload, 'secret123');
// Плохо: не проверяешь expiration
const decoded = jwt.verify(token, secret);
// Плохо: отправляешь secret на клиент
const token = jwt.sign(payload, req.headers['secret']);
✅ Правильно:
// Хорошо: включаешь только необходимые данные
jwt.sign(
{
userId: user.id,
email: user.email,
role: user.role
},
process.env.JWT_SECRET, // Из env, никогда не в коде!
{
expiresIn: '7d', // Коротко жить
algorithm: 'HS256' // Явно указываешь алгоритм
}
);
// Хорошо: валидируешь на каждый запрос
const authMiddleware = (req, res, next) => {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Unauthorized' });
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
req.user = decoded;
next();
} catch (error) {
res.status(401).json({ error: 'Token invalid' });
}
};
7. Сравнение алгоритмов
// HMAC (Symmetric) — быстро, для собственных систем
jwt.sign(payload, secret, { algorithm: 'HS256' });
// RSA (Asymmetric) — медленнее, для микросервисов
jwt.sign(payload, privateKey, { algorithm: 'RS256' });
8. Best Practices
- Храни JWT_SECRET в .env файле
- Используй HTTPS для передачи токенов
- Устанавливай разумное время жизни (не >24ч)
- Используй refresh tokens для долгоживущих сессий
- Логируй попытки использования невалидных токенов
- Включай минимально необходимую информацию в payload
- Верифицируй подпись на каждый запрос
JWT — мощный инструмент для масштабируемой аутентификации в микросервисной архитектуре.