Веб-токены JSON (сокращенно – JWT) поддерживают авторизацию и обмен информацией.
Один из самых распространенных вариантов использования этих токенов –сохранение информации о сеансе клиента после входа в систему. Информация о сеансе сохраняется локально и передается на сервер для аутентификации при выполнении запросов. При этом сервер может быть уверен в том, что клиент прошел аутентификацию.
В этой статье мы поговорим о приложениях JWT во взаимоотношениях между сервером и клиентом и рассмотрим пару примеров с Node.js и ванильным JavaScript.
Требования
Чтобы следовать этому мануалу, у вас должна быть локальная установка Node.js. Инструкции по установке зависят от вашей системы:
1: Создание токена
jsonwebtoken – это популярная реализация JWT.
Вы можете добавить ее в свой проект JavaScript, запустив в терминале следующую команду:
npm install jsonwebtoken
Далее импортируйте пакет в свои файлы:
const jwt = require('jsonwebtoken');
Чтобы подписать токен, вам потребуется 3 компонента:
- Секретный ключ токена
- Часть данных для хеширования.
- Срок действия токена.
Секретный ключ токена – это длинная строка, сгенерированная случайным образом, используемая для шифрования и дешифрования данных.
Чтобы сгенерировать этот ключ, можно использовать встроенную библиотеку Node.js под названием crypto:
> require('crypto').randomBytes(64).toString('hex')
// '09f26e402586e2faa8da4c98a35f1b20d6b033c6097befa8be3486a829587fe2f90a832bd3ff9d42710a4da095a2ce285b009f0c3730cd9b8e1af3eb84df6611'
Важно! Будьте осторожны: если ваш секретный ключ будет слишком прост, злоумышленник сможет легко взломать процесс проверки токена.
Теперь сохраните этот ключ в файле .env вашего проекта:
TOKEN_SECRET=09f26e402586e2faa8da4c98a35f1b20d6b033c60...
Чтобы перенести этот токен в файл Node.js и использовать его, сначала установите dotenv:
npm install dotenv
И импортируйте ключ в свои файлы:
const dotenv = require('dotenv');
// get config vars
dotenv.config();
// access config var
process.env.TOKEN_SECRET;
Второй компонент – данные для хеширования в токене, это может быть ID или имя пользователя (либо гораздо более сложный объект). В любом случае, это должен быть идентификатор конкретного пользователя.
Время истечения срока действия токена – третий компонент – представляет собой строку (например, 1800 секунд, или 30 минут), в которой подробно описывается, как скоро токен станет недействительным.
Вот пример функции для подписи токена:
function generateAccessToken(username) {
return jwt.sign(username, process.env.TOKEN_SECRET, { expiresIn: '1800s' });
}
Эту функцию можно отправить обратно из запроса на вход или авторизацию пользователя:
app.post('/api/createNewUser', (req, res) => {
// ...
const token = generateAccessToken({ username: req.body.username });
res.json(token);
// ...
});
В этом примере значение username берется из req (что значит request, запрос). Сам токен предоставляется здесь как res (response, ответ).
На этом мы завершаем, как jsonwebtoken, crypto и dotenv можно использовать для создания JWT.
2: Аутентификация токена
Существует много способов, чтобы внедрить систему аутентификации JWT в приложение Express.js.
Один из популярных подходов подразумевает использование промежуточного программного обеспечения (middleware) Express.js.
Оно работает следующим путем: когда запрос выполняется по определенному маршруту, вы можете отправить переменные (req, res) в промежуточную функцию перед той, которая указана в app.get((req, res) => {}).
Промежуточное ПО – это функция, которая принимает параметры (req, res, next), где:
- req – это отправленный запрос (GET, POST, DELETE, PUT и т.д.).
- res – это ответ, который можно вернуть пользователю множеством способов (res.sendStatus(200), res.json() и т.д.).
- next – это функция, которую можно вызвать, чтобы переместить выполнение за промежуточное ПО в фактический ответ сервера app.get.
Вот пример функции промежуточного ПО, которую можно использовать для аутентификации:
const jwt = require('jsonwebtoken');
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization']
const token = authHeader && authHeader.split(' ')[1]
if (token == null) return res.sendStatus(401)
jwt.verify(token, process.env.TOKEN_SECRET as string, (err: any, user: any) => {
console.log(err)
if (err) return res.sendStatus(403)
req.user = user
next()
})
}
Запрос с этой функцией промежуточного ПО будет выглядеть примерно так:
GET https://example.com:4000/api/userOrders
Authorization: Bearer JWT_ACCESS_TOKEN
А вот запрос, который будет использовать этот фрагмент промежуточного ПО:
app.get('/api/userOrders', authenticateToken, (req, res) => {
// executes after authenticateToken
// ...
})
В итоге этот код аутентифицирует токен, предоставленный клиентом. Если токен действителен, можно переходить к запросу. В противном случае запрос может быть обработан как ошибка.
3: Обработка токенов на клиентской стороне
Когда клиент получает токен, он, как правило, старается сохранить его для сбора пользовательской информации в будущих запросах.
Самый популярный способ хранения токенов аутентификации – это cookie-файлы HttpOnly.
Код JavaScript для внедрения файла cookie и хранения данных на клиентской стороне выглядит так:
// get token from fetch request
const token = await res.json();
// set token in cookie
document.cookie = `token=${token}`
При таком подходе ответы хранятся локально, где клиент может ссылаться на них в будущих запросах к серверу.
Итак, весь процесс включает в себя такие этапы: запрос и генерация токена, получение и передача его с новыми запросами, а также проверка токена и сохранение данных.
Заключение
В этом руководстве вы познакомились с токенами JWT и изучили один из вариантов их внедрения в приложение Node.js. Этот подход основан на комбинации jsonwebtoken, crypto, dotenv и express.
Конечно, существуют и другие подходы к использованию JWT. Дополнительные сведения вы найдете в документации.