Руководство по аутентификации JWT с помощью Spring Boot
Безопасность ваших приложений имеет первостепенное значение в современном цифровом мире. Одним из надежных подходов является аутентификация JWT (JSON Web Token). Он предлагает безопасный способ проверки личности пользователя. В этом руководстве мы рассмотрим реализацию аутентификации JWT в приложении Spring Boot, используя упрощенную, но эффективную методологию. Мы рассмотрим контроллеры, службы, конфигурации и репозитории, чтобы вы были хорошо подготовлены к повышению безопасности вашего приложения.
🚀 Шаг 1. Настройка проекта Spring Boot.
Начните с создания нового проекта Spring Boot или использования существующего. Ускорьте этот процесс, используя Spring Initializr, который устанавливает важные зависимости, такие как Spring Web, Spring Security и Spring Data JPA.
<!-- Include necessary dependencies in your pom.xml file --> <dependencies> <!-- Spring Web for creating web APIs --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring Security for robust authentication and authorization --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- Spring Data JPA for streamlined database interactions --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> <!-- Other dependencies... --> </dependencies>
📦 Шаг 2. Создание пользовательской сущности и репозитория.
Спроектируйте User
класс, включающий такие атрибуты id
, как username
, и password
. Разработайте UserRepository
интерфейс для облегчения управления пользовательскими данными.
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; private String password; // Getters, setters... } public interface UserRepository extends JpaRepository<User, Long> { User findByUsername(String username); }
🔒 Шаг 3. Настройка Spring Security
Создайте SecurityConfig
класс, расширяющий WebSecurityConfigurerAdapter
. Переопределение configure(HttpSecurity http)
для установки правил безопасности и управления аутентификацией JWT.
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Autowired private JwtUtil jwtUtil; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated() .and() .addFilter(new JwtAuthenticationFilter(authenticationManager(), jwtUtil)) .addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtUtil, userDetailsService)); } // Additional configurations... }
👤 Шаг 4. Реализация UserService
Разработайте UserService
интерфейс с методами загрузки пользователя по имени пользователя и сохранения нового пользователя. Реализовать UserDetailsService
получение сведений о пользователе из базы данных.
@Service public interface UserService extends UserDetailsService { UserDetails loadUserByUsername(String username); void saveUser(User user); } @Service public class UserServiceImpl implements UserService { @Autowired private UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if (user == null) { throw new UsernameNotFoundException("User not found: " + username); } return new org.springframework.security.core.userdetails.User( user.getUsername(), user.getPassword(), new ArrayList<>() ); } @Override public void saveUser(User user) { userRepository.save(user); } }
🔐 Шаг 5. Создание и проверка токенов JWT
Создайте JwtUtil
класс для генерации и проверки токенов JWT.
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.stereotype.Component; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @Component public class JwtUtil { private final String SECRET = "your-secret-key"; // Replace with a secure secret key private final long EXPIRATION_TIME = 900_000; // 15 minutes public String extractUsername(String token) { return extractClaim(token, Claims::getSubject); } public Date extractExpiration(String token) { return extractClaim(token, Claims::getExpiration); } public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) { final Claims claims = extractAllClaims(token); return claimsResolver.apply(claims); } private Claims extractAllClaims(String token) { return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody(); } public String generateToken(String username) { Map<String, Object> claims = new HashMap<>(); return createToken(claims, username); } private String createToken(Map<String, Object> claims, String subject) { return Jwts.builder() .setClaims(claims) .setSubject(subject) .setIssuedAt(new Date(System.currentTimeMillis())) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .signWith(SignatureAlgorithm.HS256, SECRET) .compact(); } public boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } public boolean validateToken(String token, UserDetails userDetails) { final String username = extractUsername(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } // Additional utility methods... }
🔑 Шаг 6. Контроллер аутентификации.
Разработайте AuthController
класс для обработки регистрации и аутентификации пользователей.
@RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserService userService; @Autowired private JwtUtil jwtUtil; @PostMapping("/register") public ResponseEntity<String> registerUser(@RequestBody User user) { userService.saveUser(user); return ResponseEntity.ok("User registered successfully!"); } @PostMapping("/login") public ResponseEntity<String> loginUser(@RequestBody AuthenticationRequest request) { try { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()) ); } catch (BadCredentialsException e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid username or password"); } UserDetails userDetails = userService.loadUserByUsername(request.getUsername()); String token = jwtUtil.generateToken(userDetails); return ResponseEntity.ok(token); } }
🔍 Шаг 7. Реализация JwtAuthenticationFilter
Создайте JwtAuthenticationFilter
класс для обработки аутентификации и авторизации JWT для каждого запроса.
@Component public class JwtAuthenticationFilter extends OncePerRequestFilter { @Autowired private UserDetailsService userDetailsService; @Autowired private JwtUtil jwtTokenUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String authorizationHeader = request.getHeader("Authorization"); String username = null; String jwtToken = null; if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { jwtToken = authorizationHeader.substring(7); try { username = jwtTokenUtil.extractUsername(jwtToken); } catch (Exception e) { // Handle token extraction/validation errors System.out.println("Error extracting username from token: " + e.getMessage()); } } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (jwtTokenUtil.validateToken(jwtToken, userDetails)) { UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } } filterChain.doFilter(request, response); } }
Заключение
Вы успешно реализовали аутентификацию JWT в своем приложении Spring Boot! Ваше приложение теперь имеет повышенную безопасность, гарантируя доступ к конфиденциальным ресурсам только авторизованным пользователям. Помните, что безопасность — это постоянный процесс, поэтому будьте в курсе лучших практик и постоянно улучшайте защиту своего приложения. Удачного кодирования и будьте в безопасности!