senior
Как настроить аутентификацию на основе JWT в Spring Security?
JWT (JSON Web Token) – компактный самодостаточный формат токена для аутентификации в REST API. Структура: HEADER.PAYLOAD.SIGNATURE.
JwtTokenProvider -- генерация и валидация токенов
@Component
public class JwtTokenProvider {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration-ms}")
private long expirationMs;
private SecretKey getSigningKey() {
return Keys.hmacShaKeyFor(Decoders.BASE64.decode(secret));
}
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("roles", userDetails.getAuthorities().stream()
.map(GrantedAuthority::getAuthority).collect(Collectors.toList()));
return Jwts.builder()
.claims(claims)
.subject(userDetails.getUsername())
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + expirationMs))
.signWith(getSigningKey())
.compact();
}
public String getUsernameFromToken(String token) {
return Jwts.parser().verifyWith(getSigningKey()).build()
.parseSignedClaims(token).getPayload().getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parser().verifyWith(getSigningKey()).build().parseSignedClaims(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}
JwtAuthenticationFilter
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
@Autowired private JwtTokenProvider tokenProvider;
@Autowired private UserDetailsService userDetailsService;
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String token = extractToken(request);
if (token != null && tokenProvider.validateToken(token)) {
String username = tokenProvider.getUsernameFromToken(token);
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
private String extractToken(HttpServletRequest request) {
String header = request.getHeader("Authorization");
if (header != null && header.startsWith("Bearer ")) {
return header.substring(7);
}
return null;
}
}
Контроллер аутентификации
Пример
@RestController
@RequestMapping("/api/auth")
public class AuthController {
@PostMapping("/login")
public ResponseEntity<AuthResponse> login(@RequestBody @Valid LoginRequest request) {
Authentication auth = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword()));
String token = tokenProvider.generateToken((UserDetails) auth.getPrincipal());
return ResponseEntity.ok(new AuthResponse(token));
}
}
На собеседовании: покажите понимание полного flow: логин -> генерация токена -> фильтр извлекает и проверяет токен -> установка SecurityContext. Частая ошибка – хранить JWT-секрет в коде, делать слишком долгий TTL access-токена (рекомендуется 15-30 минут), не обрабатывать истечение токена (500 вместо 401).