JWT (JSON Web Token)
JWT (JSON Web Token) — это компактный и безопасный стандарт для передачи информации между сторонами в виде JSON-объекта. Он часто используется для аутентификации и передачи данных в веб-приложениях. JWT токены обладают рядом преимуществ, таких как легкость использования, самодостаточность (вся необходимая информация содержится внутри токена) и поддержка подписей для проверки подлинности.
1. Что такое JWT?
JWT представляет собой строку, состоящую из трех частей, разделенных точками (.):
Эти части соответствуют:
Header (заголовок) — метаинформация о токене.
Payload (полезная нагрузка) — данные, которые содержатся в токене.
Signature (подпись) — защита токена от подделки.
2. Структура JWT
2.1 Header
Заголовок содержит информацию о типе токена и алгоритме подписи.
alg — алгоритм подписи (например, HMAC-SHA256, RS256 и др.).
typ — тип токена (обычно "JWT").
Заголовок кодируется в формате Base64Url.
2.2 Payload
Полезная нагрузка содержит данные, которые передаются в токене. Эти данные называются "claims" (утверждения).
Они делятся на три типа:
Registered claims (зарегистрированные утверждения):
iss (issuer) — кто выдал токен.
sub (subject) — пользователь или субъект токена.
aud (audience) — для кого предназначен токен.
exp (expiration time) — время истечения токена (UNIX-время).
nbf (not before) — токен не должен использоваться до указанного времени.
iat (issued at) — время выдачи токена.
jti (JWT ID) — уникальный идентификатор токена.
Public claims (публичные утверждения): настраиваемые пользователем данные, такие как role, username и т.д.
Private claims (приватные утверждения): данные, специфичные для приложения, которые не входят в публичный стандарт.
2.3 Signature
Подпись обеспечивает целостность токена. Она создается путем использования:
Закодированных в Base64Url Header и Payload.
Секретного ключа.
Алгоритма подписи.
Пример создания подписи (при использовании алгоритма HMAC-SHA256):
3. Виды шифрования и алгоритмы
JWT может быть подписан или зашифрован:
3.1 Подписанные JWT
Подписанные токены защищены от изменения, но их содержимое можно прочитать. Используемые алгоритмы:
HMAC (симметричное шифрование):
Пример: HS256 (HMAC-SHA256).
Использует один общий секретный ключ для создания и проверки подписи.
Подходит для приложений с одним центром проверки.
RSA (асимметричное шифрование):
Пример: RS256 (RSA-SHA256).
Использует пару ключей: приватный для подписания и публичный для проверки.
Применяется в системах, где проверку подписи выполняют разные сервисы.
ECDSA (эллиптическая криптография):
Пример: ES256.
Более производительный, чем RSA, но с аналогичным подходом к подписи.
3.2 Зашифрованные JWT
Иногда требуется скрыть содержимое токена. Для этого используется JSON Web Encryption (JWE). Зашифрованные токены обеспечивают конфиденциальность данных.
Основные алгоритмы шифрования:
AES (например, A256GCM).
RSA (например, RSA-OAEP).
Шифрование выполняется с использованием публичного ключа, а расшифровка — с помощью приватного.
4. Как формируется JWT?
Создается Header.
Создается Payload с данными.
Заголовок и полезная нагрузка кодируются в формате Base64Url.
Создается Signature, используя заголовок, полезную нагрузку и секретный ключ.
Все части объединяются в строку через точки.
Пример токена:
#Java #Training #Spring #Security #JWT
JWT (JSON Web Token) — это компактный и безопасный стандарт для передачи информации между сторонами в виде JSON-объекта. Он часто используется для аутентификации и передачи данных в веб-приложениях. JWT токены обладают рядом преимуществ, таких как легкость использования, самодостаточность (вся необходимая информация содержится внутри токена) и поддержка подписей для проверки подлинности.
1. Что такое JWT?
JWT представляет собой строку, состоящую из трех частей, разделенных точками (.):
xxxxx.yyyyy.zzzzz
Эти части соответствуют:
Header (заголовок) — метаинформация о токене.
Payload (полезная нагрузка) — данные, которые содержатся в токене.
Signature (подпись) — защита токена от подделки.
2. Структура JWT
2.1 Header
Заголовок содержит информацию о типе токена и алгоритме подписи.
{
"alg": "HS256",
"typ": "JWT"
}
alg — алгоритм подписи (например, HMAC-SHA256, RS256 и др.).
typ — тип токена (обычно "JWT").
Заголовок кодируется в формате Base64Url.
2.2 Payload
Полезная нагрузка содержит данные, которые передаются в токене. Эти данные называются "claims" (утверждения).
Они делятся на три типа:
Registered claims (зарегистрированные утверждения):
iss (issuer) — кто выдал токен.
sub (subject) — пользователь или субъект токена.
aud (audience) — для кого предназначен токен.
exp (expiration time) — время истечения токена (UNIX-время).
nbf (not before) — токен не должен использоваться до указанного времени.
iat (issued at) — время выдачи токена.
jti (JWT ID) — уникальный идентификатор токена.
Public claims (публичные утверждения): настраиваемые пользователем данные, такие как role, username и т.д.
Private claims (приватные утверждения): данные, специфичные для приложения, которые не входят в публичный стандарт.
{
"sub": "1234567890",
"name": "John Doe",
"admin": true,
"exp": 1701234567
}
Payload также кодируется в формате Base64Url.
2.3 Signature
Подпись обеспечивает целостность токена. Она создается путем использования:
Закодированных в Base64Url Header и Payload.
Секретного ключа.
Алгоритма подписи.
Пример создания подписи (при использовании алгоритма HMAC-SHA256):
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)
Результат (подпись) добавляется в конец токена.
3. Виды шифрования и алгоритмы
JWT может быть подписан или зашифрован:
3.1 Подписанные JWT
Подписанные токены защищены от изменения, но их содержимое можно прочитать. Используемые алгоритмы:
HMAC (симметричное шифрование):
Пример: HS256 (HMAC-SHA256).
Использует один общий секретный ключ для создания и проверки подписи.
Подходит для приложений с одним центром проверки.
RSA (асимметричное шифрование):
Пример: RS256 (RSA-SHA256).
Использует пару ключей: приватный для подписания и публичный для проверки.
Применяется в системах, где проверку подписи выполняют разные сервисы.
ECDSA (эллиптическая криптография):
Пример: ES256.
Более производительный, чем RSA, но с аналогичным подходом к подписи.
3.2 Зашифрованные JWT
Иногда требуется скрыть содержимое токена. Для этого используется JSON Web Encryption (JWE). Зашифрованные токены обеспечивают конфиденциальность данных.
Основные алгоритмы шифрования:
AES (например, A256GCM).
RSA (например, RSA-OAEP).
Шифрование выполняется с использованием публичного ключа, а расшифровка — с помощью приватного.
4. Как формируется JWT?
Создается Header.
Создается Payload с данными.
Заголовок и полезная нагрузка кодируются в формате Base64Url.
Создается Signature, используя заголовок, полезную нагрузку и секретный ключ.
Все части объединяются в строку через точки.
Пример токена:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImV4cCI6MTcwMTIzNDU2N30
.
sX5yQNjQp17l-lF6fBt6CXyP0NItBTppS-ANkSQ6jCk
#Java #Training #Spring #Security #JWT
5. Что необходимо для расшифровки и проверки?
5.1 Для подписи на основе HMAC:
Алгоритм (например, HS256).
Секретный ключ (shared secret).
5.2 Для подписи на основе RSA:
Алгоритм (например, RS256).
Публичный ключ (для проверки подписи).
Приватный ключ (для создания подписи, если вы являетесь сервером).
5.3 Для шифрования:
Алгоритм (например, AES256 или RSA-OAEP).
Публичный ключ (для расшифровки).
6. Преимущества JWT
Самодостаточность: Вся необходимая информация содержится в токене.
Безопасность: Подпись обеспечивает защиту от подделки.
Простота использования: Легко передается между клиентом и сервером.
Масштабируемость: Подходит для распределенных систем (например, микросервисов).
7. Уязвимости и меры безопасности
Использование слабых секретных ключей: Используйте сложные и длинные ключи.
Отсутствие проверки срока действия (exp): Убедитесь, что токен истекает через разумный период.
Применение неподходящих алгоритмов: Используйте надежные алгоритмы (например, RS256, HS256).
Утечка приватного ключа: Приватные ключи должны быть строго защищены.
XSS-атаки: Храните JWT в HttpOnly cookies, а не в localStorage.
8. Пример реализации на Java
Пример генерации и проверки JWT с использованием библиотеки jjwt (Java JWT):
Зависимость в pom.xml:
Генерация токена:
Проверка токена:
#Java #Training #Spring #Security #JWT
5.1 Для подписи на основе HMAC:
Алгоритм (например, HS256).
Секретный ключ (shared secret).
5.2 Для подписи на основе RSA:
Алгоритм (например, RS256).
Публичный ключ (для проверки подписи).
Приватный ключ (для создания подписи, если вы являетесь сервером).
5.3 Для шифрования:
Алгоритм (например, AES256 или RSA-OAEP).
Публичный ключ (для расшифровки).
6. Преимущества JWT
Самодостаточность: Вся необходимая информация содержится в токене.
Безопасность: Подпись обеспечивает защиту от подделки.
Простота использования: Легко передается между клиентом и сервером.
Масштабируемость: Подходит для распределенных систем (например, микросервисов).
7. Уязвимости и меры безопасности
Использование слабых секретных ключей: Используйте сложные и длинные ключи.
Отсутствие проверки срока действия (exp): Убедитесь, что токен истекает через разумный период.
Применение неподходящих алгоритмов: Используйте надежные алгоритмы (например, RS256, HS256).
Утечка приватного ключа: Приватные ключи должны быть строго защищены.
XSS-атаки: Храните JWT в HttpOnly cookies, а не в localStorage.
8. Пример реализации на Java
Пример генерации и проверки JWT с использованием библиотеки jjwt (Java JWT):
Зависимость в pom.xml:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
Генерация токена:
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
public class JwtExample {
private static final String SECRET_KEY = "your-secret-key";
public static String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 3600000)) // 1 час
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
}
Проверка токена:
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.Claims;
public class JwtExample {
private static final String SECRET_KEY = "your-secret-key";
public static Claims validateToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
}
#Java #Training #Spring #Security #JWT
Внедрение JWT-аутентификации в Spring Security
JWT (JSON Web Token) аутентификация — это популярный подход к обеспечению безопасности веб-приложений, особенно в REST API. Она позволяет передавать информацию о пользователе в токене и исключает необходимость поддерживать сессии на сервере.
1. Общий процесс JWT-аутентификации
Аутентификация пользователя: Клиент отправляет логин и пароль на сервер.
Генерация токена: После успешной аутентификации сервер создает JWT токен и возвращает его клиенту.
Доступ к защищенным ресурсам: Клиент отправляет токен в заголовке Authorization (Bearer <token>), чтобы получить доступ к ресурсам.
Проверка токена: Сервер проверяет токен при каждом запросе и предоставляет доступ только аутентифицированным пользователям.
2. Необходимые зависимости
Добавьте в pom.xml зависимости:
3. Шаги реализации JWT-аутентификации
3.1 Создание конфигурационного класса Spring Security
Создаем класс SecurityConfig для настройки Spring Security:
#Java #Training #Spring #Security #JWT
JWT (JSON Web Token) аутентификация — это популярный подход к обеспечению безопасности веб-приложений, особенно в REST API. Она позволяет передавать информацию о пользователе в токене и исключает необходимость поддерживать сессии на сервере.
1. Общий процесс JWT-аутентификации
Аутентификация пользователя: Клиент отправляет логин и пароль на сервер.
Генерация токена: После успешной аутентификации сервер создает JWT токен и возвращает его клиенту.
Доступ к защищенным ресурсам: Клиент отправляет токен в заголовке Authorization (Bearer <token>), чтобы получить доступ к ресурсам.
Проверка токена: Сервер проверяет токен при каждом запросе и предоставляет доступ только аутентифицированным пользователям.
2. Необходимые зависимости
Добавьте в pom.xml зависимости:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
3. Шаги реализации JWT-аутентификации
3.1 Создание конфигурационного класса Spring Security
Создаем класс SecurityConfig для настройки Spring Security:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
private final JwtAuthenticationFilter jwtAuthenticationFilter;
public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
this.jwtAuthenticationFilter = jwtAuthenticationFilter;
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/auth/login", "/auth/register").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
return configuration.getAuthenticationManager();
}
}
#Java #Training #Spring #Security #JWT
3.2 Создание модели пользователя
Создаем сущности User и Role для хранения пользователей и ролей в базе данных.
3.3 Создание сервиса для загрузки пользователя
Создаем сервис, который будет находить пользователя в базе данных и предоставлять его данные Spring Security.
3.4 Создание утилиты для работы с JWT
Создаем класс JwtUtils для генерации и проверки токенов.
#Java #Training #Spring #Security #JWT
Создаем сущности User и Role для хранения пользователей и ролей в базе данных.
import javax.persistence.*;
import java.util.Set;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles;
// Getters and Setters
}
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// Getters and Setters
}
3.3 Создание сервиса для загрузки пользователя
Создаем сервис, который будет находить пользователя в базе данных и предоставлять его данные Spring Security.
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class CustomUserDetailsService implements UserDetailsService {
private final UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("User not found"));
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUsername())
.password(user.getPassword())
.authorities(user.getRoles().stream().map(Role::getName).toArray(String[]::new))
.build();
}
}
3.4 Создание утилиты для работы с JWT
Создаем класс JwtUtils для генерации и проверки токенов.
import io.jsonwebtoken.*;
import org.springframework.stereotype.Component;
import java.util.Date;
@Component
public class JwtUtils {
private final String jwtSecret = "your-secret-key";
private final long jwtExpirationMs = 86400000; // 24 часа
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + jwtExpirationMs))
.signWith(SignatureAlgorithm.HS256, jwtSecret)
.compact();
}
public String extractUsername(String token) {
return Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}
#Java #Training #Spring #Security #JWT
3.5 Создание фильтра для проверки JWT
Фильтр проверяет JWT токен в каждом запросе.
3.6 Контроллер для аутентификации
Создаем контроллер для входа и регистрации пользователей.
#Java #Training #Spring #Security #JWT
Фильтр проверяет JWT токен в каждом запросе.
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private final JwtUtils jwtUtils;
private final CustomUserDetailsService userDetailsService;
public JwtAuthenticationFilter(JwtUtils jwtUtils, CustomUserDetailsService userDetailsService) {
this.jwtUtils = jwtUtils;
this.userDetailsService = userDetailsService;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
String jwt = authHeader.substring(7);
if (jwtUtils.validateToken(jwt)) {
String username = jwtUtils.extractUsername(jwt);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
filterChain.doFilter(request, response);
}
}
3.6 Контроллер для аутентификации
Создаем контроллер для входа и регистрации пользователей.
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/auth")
public class AuthController {
private final AuthenticationManager authenticationManager;
private final JwtUtils jwtUtils;
public AuthController(AuthenticationManager authenticationManager, JwtUtils jwtUtils) {
this.authenticationManager = authenticationManager;
this.jwtUtils = jwtUtils;
}
@PostMapping("/login")
public String login(@RequestBody AuthRequest authRequest) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword())
);
SecurityContextHolder.getContext().setAuthentication(authentication);
return jwtUtils.generateToken(authRequest.getUsername());
}
}
#Java #Training #Spring #Security #JWT