Сьогодні займемося практичним впровадженням JWT у наш додаток. Додамо генерацію й валідацію токенів, налаштуємо фільтри безпеки та забезпечимо захист REST API. Для цього створимо невеликий Spring Boot додаток, де авторизація відбуватиметься через JWT.
JWT — це не просто модне слово. Це зручний, легкий і широко вживаний механізм аутентифікації в сучасних веб-додатках. Наприклад, якщо ти розробник і йдеш на співбесіду, будь певен: запитають «розкажи про JWT». А якщо ні (пощастило!), то в реальному житті ти з ним зіткнешся на будь-якому проєкті, пов'язаному з аутентифікацією.
План нашого додатку
Наш додаток складатиметься з двох основних функціональностей:
- Аутентифікація — користувач надсилає свої облікові дані (логін і пароль), а сервер повертає JWT.
- Доступ до захищених ресурсів — користувач передає JWT у заголовку запитів, і якщо токен валідний, сервер надає доступ до ресурсу.
Крок 1. Підготовка: конфігурація проєкту
Спочатку створимо Spring Boot проєкт. Ми будемо використовувати:
- Maven для керування залежностями.
- Spring Boot версії 2.7+.
- Залежності:
spring-boot-starter-security,spring-boot-starter-web,jjwt(Java JWT бібліотека).
Конфігурація Maven Створи проєкт за допомогою Spring Initializr (https://start.spring.io/). Додай такі залежності у pom.xml:
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Starter Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Java JWT бібліотека -->
<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>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
</dependencies>
Крок 2. Налаштування SecurityConfig
Потрібно налаштувати безпеку й прибрати стандартну форму логіну Spring Security. Замiнимо її авторизацією через JWT.
Створи клас SecurityConfig:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll() // Доступ до цих маршрутів без авторизації
.anyRequest().authenticated() // Всі інші маршрути потребують авторизації
.and()
.httpBasic();
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
Ми вимкнули CSRF (лише для простого навчального прикладу), налаштували доступ до ендпоінтів /api/auth/** без авторизації і вимагатимемо авторизацію для всіх інших запитів.
Крок 3. Створення сутності користувача
Потрібно створити сутність користувача, щоб змоделювати процес аутентифікації. Для простоти зберігатимемо користувачів в пам'яті.
public class User {
private String username;
private String password;
// Конструктори, геттери і сеттери
public User(String username, String password) {
this.username = username;
this.password = password;
}
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
}
Крок 4. Створення сервісу для генерації JWT
Тепер реалізуємо сервіс, який генеруватиме JWT.
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Service;
import java.util.Date;
@Service
public class JwtService {
private final String SECRET_KEY = "your-256-bit-secret"; // Ніколи не зберігаємо секрети в коді в реальних проєктах
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 30)) // 30 хвилин
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
}
Крок 5. Контролер для аутентифікації
Створимо контролер, який оброблятиме запити для аутентифікації.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@Autowired
private JwtService jwtService;
@Autowired
private PasswordEncoder passwordEncoder;
@PostMapping("/login")
public String login(@RequestBody User loginRequest) {
// Приклад: статичний користувач для тестів
User user = new User("admin", passwordEncoder.encode("password"));
if (user.getUsername().equals(loginRequest.getUsername()) &&
passwordEncoder.matches(loginRequest.getPassword(), user.getPassword())) {
return jwtService.generateToken(user.getUsername());
}
throw new RuntimeException("Невірні облікові дані");
}
}
Цей ендпоінт приймає username і password, порівнює їх із даними користувача (в даному випадку закодованими в пам'яті), і якщо все валідно, повертає JWT.
Крок 6. Валідація JWT
Тепер додамо фільтр, який оброблятиме кожен запит, перевіряючи переданий токен.
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
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 String SECRET_KEY = "your-256-bit-secret";
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7); // Видаляємо "Bearer "
try {
Claims claims = Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
String username = claims.getSubject();
// Встановлюємо автентифікацію
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(username, null, null);
SecurityContextHolder.getContext().setAuthentication(auth);
} catch (Exception e) {
System.out.println("Недійсний токен.");
}
}
filterChain.doFilter(request, response);
}
}
Не забудь зареєструвати цей фільтр у конфігурації.
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
return http.build();
}
Крок 7. Тестування
Тепер можна протестувати додаток:
- Надішли POST запит на
/api/auth/loginз JSON:{ "username": "admin", "password": "password" }Отримаєш JWT у відповіді.
- Використай цей JWT у заголовку
Authorizationдля будь-якого захищеного маршруту:Authorization: Bearer <твій-токен>
Ось і все! Ми захистили наш REST API за допомогою JWT. У реальному житті тобі знадобиться додати багато покращень: зберігання користувачів у базі даних, обробка Refresh токенів та багато іншого. Але тепер ти знаєш основні принципи.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ