Java for Beginner
677 subscribers
560 photos
156 videos
12 files
860 links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
JWT (JSON Web Token)

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:
<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 зависимости:
<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 для хранения пользователей и ролей в базе данных.
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 токен в каждом запросе.
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