JWT (JSON Web Token) — это компактный и самодостаточный способ передачи информации между сторонами в виде JSON-объекта. JWT часто используется для аутентификации и авторизации в веб-приложениях. Давайте разберем, как работает JWT-аутентификация.
JWT состоит из трех частей, разделенных точками (.
):
Заголовок содержит информацию о типе токена и алгоритме шифрования.
{
"alg": "HS256",
"typ": "JWT"
}
Полезная нагрузка содержит claims (утверждения), которые могут быть зарегистрированными, публичными или приватными.
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
Подпись используется для проверки того, что токен не был изменен. Она создается путем кодирования заголовка и полезной нагрузки с использованием секретного ключа.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
Пользователь отправляет свои учетные данные (например, имя пользователя и пароль) на сервер для аутентификации.
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody LoginRequest loginRequest) {
User user = userService.authenticate(loginRequest.getUsername(), loginRequest.getPassword());
if (user != null) {
String token = jwtTokenUtil.generateToken(user);
return ResponseEntity.ok(token);
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
}
}
Если аутентификация успешна, сервер генерирует JWT и возвращает его клиенту.
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
public class JwtTokenUtil {
private String secret = "mySecretKey";
public String generateToken(User user) {
return Jwts.builder()
.setSubject(user.getUsername())
.claim("roles", user.getRoles())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 1 час
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
}
Клиент сохраняет JWT (обычно в localStorage или cookies) и включает его в заголовок Authorization
при каждом запросе к защищенным ресурсам.
Authorization: Bearer <JWT>
Сервер проверяет JWT на каждом запросе, чтобы убедиться, что токен действителен и не истек.
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
public class JwtTokenUtil {
private String secret = "mySecretKey";
public Claims validateToken(String token) {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}
}
Сервер проверяет JWT и разрешает доступ к защищенным ресурсам, если токен действителен.
@GetMapping("/protected")
public ResponseEntity<String> protectedResource(@RequestHeader("Authorization") String authHeader) {
String token = authHeader.substring(7); // Убираем "Bearer "
Claims claims = jwtTokenUtil.validateToken(token);
if (claims != null) {
return ResponseEntity.ok("Access granted to protected resource");
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid token");
}
}
Рассмотрим пример полной реализации JWT-аутентификации в Spring Boot.
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.Claims;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
public class JwtTokenUtil {
private String secret = "mySecretKey";
public String generateToken(User user) {
return Jwts.builder()
.setSubject(user.getUsername())
.claim("roles", user.getRoles())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60)) // 1 час
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}
public Claims validateToken(String token) {
return Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
}
}
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/auth")
public class AuthController {
private final UserService userService;
private final JwtTokenUtil jwtTokenUtil;
public AuthController(UserService userService, JwtTokenUtil jwtTokenUtil) {
this.userService = userService;
this.jwtTokenUtil = jwtTokenUtil;
}
@PostMapping("/login")
public ResponseEntity<String> login(@RequestBody LoginRequest loginRequest) {
User user = userService.authenticate(loginRequest.getUsername(), loginRequest.getPassword());
if (user != null) {
String token = jwtTokenUtil.generateToken(user);
return ResponseEntity.ok(token);
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");
}
}
@GetMapping("/protected")
public ResponseEntity<String> protectedResource(@RequestHeader("Authorization") String authHeader) {
String token = authHeader.substring(7); // Убираем "Bearer "
Claims claims = jwtTokenUtil.validateToken(token);
if (claims != null) {
return ResponseEntity.ok("Access granted to protected resource");
} else {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid token");
}
}
}
JWT-аутентификация — это мощный и гибкий способ управления аутентификацией и авторизацией в веб-приложениях. JWT состоит из трех частей: заголовка, полезной нагрузки и подписи. Процесс аутентификации включает генерацию токена, его передачу и верификацию на сервере. JWT обеспечивает безопасность, компактность и отсутствие необходимости хранения состояния на сервере, что делает его популярным выбором для современных веб-приложений.