Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как проверял токен в приложении?
Проверка токенов — одна из ключевых задач аутентификации и авторизации в backend приложениях. За 10+ лет работы с Node.js я использовал различные подходы для валидации токенов.
Типы токенов
Основные типы токенов, которые я проверял:
- JWT (JSON Web Token) — самодостаточные токены с подписью
- Bearer токены — простые строки, хранящиеся на сервере
- API ключи — статичные ключи для machine-to-machine коммуникации
- Refresh токены — для обновления доступа
- Session токены — для web приложений
JWT проверка
const jwt = require('jsonwebtoken');
// Проверка JWT токена
function verifyJWT(token) {
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
algorithms: ['HS256'],
issuer: 'myapp',
audience: 'myusers'
});
return { valid: true, decoded };
} catch (error) {
return {
valid: false,
error: error.message // TokenExpiredError, JsonWebTokenError
};
}
}
// Middleware для проверки токена
const authMiddleware = (req, res, next) => {
const authHeader = req.headers['authorization'];
if (!authHeader) {
return res.status(401).json({ error: 'Missing token' });
}
const token = authHeader.replace('Bearer ', '');
const result = verifyJWT(token);
if (!result.valid) {
return res.status(401).json({ error: result.error });
}
req.user = result.decoded;
next();
};
Проверка с использованием Passport.js
Для более сложных сценариев использую Passport с стратегиями:
const passport = require('passport');
const JwtStrategy = require('passport-jwt').Strategy;
const ExtractJwt = require('passport-jwt').ExtractJwt;
passport.use(new JwtStrategy(
{
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET,
algorithms: ['HS256']
},
(payload, done) => {
// Проверить пользователя в БД
User.findById(payload.sub)
.then(user => {
if (user) return done(null, user);
return done(null, false);
})
.catch(err => done(err));
}
));
// Использование в маршруте
router.get('/profile', passport.authenticate('jwt'), (req, res) => {
res.json({ user: req.user });
});
Express.js middleware для проверки
interface JwtPayload {
sub: string;
email: string;
iat: number;
exp: number;
}
function createAuthMiddleware(secret: string) {
return (req: Request, res: Response, next: NextFunction) => {
try {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: 'Unauthorized' });
}
const payload = jwt.verify(token, secret) as JwtPayload;
// Проверить дополнительные условия
if (payload.exp < Date.now() / 1000) {
return res.status(401).json({ error: 'Token expired' });
}
req.user = payload;
next();
} catch (error) {
if (error instanceof jwt.TokenExpiredError) {
return res.status(401).json({ error: 'Token expired' });
}
if (error instanceof jwt.JsonWebTokenError) {
return res.status(401).json({ error: 'Invalid token' });
}
res.status(500).json({ error: 'Internal error' });
}
};
}
Проверка с учётом роли пользователя (RBAC)
const authorize = (roles = []) => {
return (req, res, next) => {
if (!req.user) {
return res.status(401).json({ error: 'Unauthorized' });
}
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
};
};
// Использование
router.delete('/users/:id',
authenticate,
authorize(['admin']),
deleteUserHandler
);
Redis для кеширования токенов
Для высоконагруженных систем кешировал информацию о токенах:
const redis = require('redis');
const client = redis.createClient();
async function verifyTokenWithCache(token) {
// Проверить в Redis
const cached = await client.get(`token:${token}`);
if (cached) {
return JSON.parse(cached);
}
// Если нет в кеше, проверить подпись
const decoded = jwt.verify(token, process.env.JWT_SECRET);
// Сохранить на (exp - now) секунд
const ttl = decoded.exp - Math.floor(Date.now() / 1000);
await client.setex(`token:${token}`, ttl, JSON.stringify(decoded));
return decoded;
}
Проверка API ключей
const apiKeyMiddleware = (req, res, next) => {
const apiKey = req.headers['x-api-key'];
if (!apiKey) {
return res.status(401).json({ error: 'Missing API key' });
}
// Проверить в БД (хеш для безопасности)
const hashedKey = crypto
.createHash('sha256')
.update(apiKey)
.digest('hex');
ApiKey.findOne({ hash: hashedKey })
.then(record => {
if (!record) {
return res.status(401).json({ error: 'Invalid API key' });
}
if (record.expiresAt < new Date()) {
return res.status(401).json({ error: 'API key expired' });
}
req.apiKeyUser = record.userId;
next();
})
.catch(() => res.status(500).json({ error: 'Error' }));
};
Черный список токенов (отзыв)
// Logout — добавить в черный список
router.post('/logout', authenticate, (req, res) => {
const token = req.headers.authorization.split(' ')[1];
const decoded = jwt.decode(token);
const ttl = decoded.exp - Math.floor(Date.now() / 1000);
// Добавить в Redis на время до истечения
client.setex(`blacklist:${token}`, ttl, 'true');
res.json({ message: 'Logged out' });
});
// Проверка при верификации
function verifyJWT(token) {
const inBlacklist = client.get(`blacklist:${token}`);
if (inBlacklist) {
return { valid: false, error: 'Token revoked' };
}
try {
return { valid: true, decoded: jwt.verify(token, secret) };
} catch (err) {
return { valid: false, error: err.message };
}
}
Best Practices при проверке токенов
- Всегда используй HTTPS для передачи токенов
- Никогда не логируй токены в logs
- Установи правильное время жизни (exp) для токенов
- Используй секретные ключи из переменных окружения
- Кешируй результаты проверки в Redis для оптимизации
- Реализуй механизм отзыва токенов (blacklist или whitelist)
- Валидируй все claims в JWT (iss, aud, sub, exp)
- Используй асимметричную криптографию (RS256) для микросервисов
- Следи за временем истечения и предупреждай пользователей
- Логируй попытки с невалидными токенами для безопасности
Правильная проверка токенов — основа безопасности приложения. Я всегда использовал комбинацию проверок уровня приложения, кеширования и мониторинга для максимальной защиты.