JWT для програмістів: що це таке і як правильно використовувати
Якщо ви програміст і працюєте з веб-додатками, то напевно чули про JWT (JSON Web Token). Але багато розробників досі не розуміють, що це насправді таке і коли його варто використовувати. Можливо, ви навіть намагалися його впровадити, але зіткнулися з питаннями: чому JWT краще сесій? Коли його НЕ варто використовувати? Як зробити це безпечно? У цій статті ми розберемо JWT простою мовою, з реальними прикладами і практичними рекомендаціями.
⸻
Що таке JWT простими словами
Уявіте, що ви йдете в клуб. На вході вам дають браслет з печаткою, який підтверджує, що ви заплатили за вхід. Цей браслет містить інформацію про вас (ім'я, вік) і печатку клубу, яку неможливо підробити. З цим браслетом ви можете ходити по всьому клубу, заходити в VIP-зону, якщо у вас є відповідний статус, і вам не потрібно щоразу підходити до каси і доводити, що ви заплатили.
JWT працює аналогічно — це цифровий "браслет", який містить інформацію про користувача і захищений цифровим підписом. Сервер видає цей токен після успішної авторизації, і клієнт використовує його для доступу до захищених ресурсів.
JWT складається з трьох частин, розділених крапками:
• Header — містить інформацію про тип токена і алгоритм підпису
• Payload — містить дані про користувача (ID, ролі, права доступу)
• Signature — цифровий підпис, який гарантує, що токен не був змінений
Виглядає це приблизно так: `xxxxx.yyyyy.zzzzz`
⸻
Реальні проблеми, які вирішує JWT
Проблема #1: Масштабування додатка
Уявіте інтернет-магазин з мільйонами користувачів. При традиційному підході з сесіями, сервер повинен зберігати інформацію про кожну активну сесію в пам'яті або базі даних. Коли у вас 100,000 одночасних користувачів, це стає серйозною проблемою.
👉 Приклад:
• Кожна сесія займає ~1KB пам'яті
• 100,000 користувачів = 100MB тільки на сесії
• Плюс час на запити до бази для перевірки кожної сесії
JWT вирішує цю проблему, бо вся необхідна інформація зберігається в самому токені. Сервер не повинен нічого зберігати — він просто перевіряє підпис токена і отримує всю необхідну інформацію.
Проблема #2: Робота з мікросервісами
Сучасні додатки часто складаються з декількох сервісів. Наприклад, у вас є сервіс користувачів, сервіс товарів, сервіс платежів. З традиційними сесіями кожен сервіс повинен звертатися до центральної бази даних, щоб перевірити, чи авторизований користувач.
⚡ Наприклад: користувач хоче купити товар. Йому потрібно пройти через 4 сервіси, і кожен з них робить запит до бази користувачів. Це 4 додаткових запити!
З JWT кожен сервіс може самостійно перевірити токен, не звертаючись до інших сервісів. Це значно спрощує архітектуру і підвищує продуктивність.
Проблема #3: Мобільні додатки і API
Мобільні додатки не підтримують cookies так само добре, як браузери. JWT ідеально підходить для мобільних додатків, бо це просто рядок, який можна зберегти в локальній пам'яті додатка.
⸻
Крок за кроком: як працює JWT
Крок 1. Користувач логіниться
Користувач вводить email і пароль. Сервер перевіряє ці дані у базі.
👉 Приклад коду на Java:
```java
@PostMapping("/login")
public ResponseEntity
User user = userService.authenticate(request.getEmail(), request.getPassword());
if (user != null) {
String token = createToken(user);
return ResponseEntity.ok(token);
}
return ResponseEntity.status(401).body("Invalid credentials");
}
```
Крок 2. Сервер створює JWT токен
Якщо дані правильні, сервер створює токен з інформацією про користувача:
```java
public String createToken(User user) {
return Jwts.builder()
.setSubject(user.getEmail())
.claim("userId", user.getId())
.claim("role", user.getRole())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 86400000)) // 24 години
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
}
```
Крок 3. Клієнт зберігає токен
Клієнт отримує токен і зберігає його (зазвичай в localStorage або у змінній додатка).
Крок 4. Клієнт використовує токен
При кожному запиті до захищених ресурсів клієнт відправляє токен в заголовку Authorization:
```
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
Крок 5. Сервер перевіряє токен
Сервер перевіряє підпис токена і витягує з нього інформацію про користувача:
```java
public Claims validateToken(String token) {
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
}
```
⸻
Реальні приклади використання
Приклад 1: Інтернет-магазин електроніки
Розглянемо типовий сценарій в інтернет-магазині:
• Користувач логіниться
• Додає товари в кошик
• Переходить до оформлення замовлення
• Робить платіж
Кожна з цих дій потребує авторизації. З JWT ви можете включити в токен:
```java
public String createShopToken(User user) {
return Jwts.builder()
.setSubject(user.getEmail())
.claim("userId", user.getId())
.claim("role", user.getRole()) // "customer", "admin", "manager"
.claim("cartId", user.getCurrentCartId())
.claim("loyaltyLevel", user.getLoyaltyLevel()) // для знижок
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1 година
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
}
```
⚡ Переваги: сервіс кошика може самостійно перевірити, чи належить кошик користувачу, без запитів до бази користувачів.
Приклад 2: API для соціальної мережі
Уявіте, що ви розробляєте API для соціальної мережі на кшталт Instagram. Користувачі можуть:
• Переглядати стрічку новин
• Публікувати фото
• Коментувати пости
• Відправляти особисті повідомлення
👉 Приклад токена для соціальної мережі:
```java
public String createSocialToken(User user) {
return Jwts.builder()
.setSubject(user.getUsername())
.claim("userId", user.getId())
.claim("role", user.getRole())
.claim("verified", user.isVerified()) // верифікований акаунт
.claim("permissions", user.getPermissions()) // що може робити
.claim("followersCount", user.getFollowersCount()) // для рекомендацій
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1800000)) // 30 хвилин
.signWith(SignatureAlgorithm.HS256, secretKey)
.compact();
}
```
⸻
JWT vs інші методи аутентифікації
Порівняння з традиційними сесіями
**Традиційні сесії:**
👍 Плюси:
• Сервер має повний контроль над сесією
• Можна миттєво відкликати доступ
• Менший розмір cookies (тільки session ID)
• Більш захищені від XSS атак
👎 Мінуси:
• Потребують зберігання на сервері
• Складніше масштабувати
• Проблеми з мікросервісами
• Не підходять для мобільних додатків
**JWT:**
👍 Плюси:
• Не потребує зберігання на сервері
• Легко масштабується
• Ідеально для мікросервісів
• Підходить для мобільних додатків
• Містить всю необхідну інформацію
👎 Мінуси:
• Неможливо миттєво відкликати
• Більший розмір
• Складніше оновлювати дані користувача
• Потенційно менш захищені
Порівняння з OAuth
OAuth — це не альтернатива JWT, а протокол авторизації, який часто використовує JWT як формат токенів. OAuth вирішує проблему надання доступу третім сторонам (наприклад, коли ви логінитесь через Google або Facebook).
⸻
Коли використовувати JWT
Ідеальні сценарії:
• **Мікросервісна архітектура** — коли у вас багато незалежних сервісів
• **API для мобільних додатків** — JWT легко зберігати і передавати
• **Одноразова авторизація** — коли користувач рідко змінює свої дані
• **Розподілені системи** — коли сервери не поділяють спільну базу даних
• **Короткострокові токени** — для сесій тривалістю до кількох годин
Коли краще НЕ використовувати JWT:
• **Довготривалі сесії** — для сесій, що тривають днями або тижнями
• **Часті зміни даних користувача** — коли ролі і права часто оновлюються
• **Строгі вимоги безпеки** — коли потрібна можливість миттєво відкликати доступ
• **Обмежена пропускна здатність** — JWT токени більші за session ID
⚠️ Важливо: якщо ви сумніваєтесь, почніть з традиційних сесій. Їх легше налаштувати і вони безпечніші за замовчуванням.
⸻
Найкращі практики безпеки
1. Завжди використовуйте HTTPS
JWT токени містять чутливу інформацію в незашифрованому вигляді. Завжди передавайте їх через HTTPS.
2. Встановлюйте короткий термін дії
Рекомендується встановлювати термін дії токена 15-60 хвилин для звичайних операцій.
👉 Приклад:
```java
.setExpiration(new Date(System.currentTimeMillis() + 900000)) // 15 хвилин
```
3. Використовуйте Refresh токени
Для довготривалого доступу використовуйте систему з двома токенами: коротким access токеном і довшим refresh токеном.
4. Зберігайте токени безпечно
У браузері краще зберігати токени в httpOnly cookies, а не в localStorage, щоб захистити від XSS атак.
5. Валідуйте всі поля
Завжди перевіряйте не тільки підпис токена, а й термін дії, аудиторію та інші claims:
```java
public boolean isTokenValid(String token) {
try {
Claims claims = Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
// Перевірка терміну дії
return !claims.getExpiration().before(new Date());
} catch (Exception e) {
return false;
}
}
```
⸻
Що це означає для розробника
1. JWT не магія. Це лише інструмент, який спрощує роботу з авторизацією, але не замінює якісну архітектуру та безпеку.
2. Не всі проекти потребують JWT. Для простих веб-додатків традиційні сесії можуть бути кращим вибором.
3. Безпека — це не про JWT vs сесії. Це про правильну реалізацію, HTTPS, валідацію та захист від атак.
4. Масштабування важливе, але не завжди. Якщо у вас 1000 користувачів, проблеми масштабування вас не торкнуться найближчі роки.
⸻
Висновок
JWT — це потужний інструмент для сучасної веб-розробки, але він не є універсальним рішенням. Вибір між JWT і традиційними сесіями залежить від ваших конкретних потреб: для мікросервісів і API JWT ідеальний варіант, для традиційних веб-додатків сесії можуть бути кращим вибором, для мобільних додатків JWT практично незамінний.
Головне — розуміти переваги і недоліки кожного підходу і вибирати найкращий інструмент для вашої конкретної задачі. JWT не зробить ваш додаток автоматично кращим, але при правильному використанні може значно спростити архітектуру і підвищити продуктивність.
Пам'ятайте: безпека завжди повинна бути пріоритетом. Незалежно від того, який метод аутентифікації ви обираєте, завжди дотримуйтесь найкращих практик безпеки і регулярно оновлюйте свої знання в цій сфері.