Crear un filtro para interceptar las peticiones HTTP entrantes a los controladores REST de Spring Boot.
Este filtro será el encargado de:
- Detectar la cabecera
Authorization
. - Extraer el
token JWT
de la cabeceraAuthorization
. - Verificar la firma del token, debe estar firmado por el backend con la misma clave secreta.
- Decodificar el token y extraer el payload, por ejemplo el id del usuario.
- Comprobar que existe el usuario y obtenerlo.
- Cargar el usuario en el contexto de seguridad de Spring Security para notificar a Spring del usuario autenticado.
Crear filtro Spring
Se crea una clase Java que extiende de OncePerRequestFilter
:
@Component
@AllArgsConstructor
@Slf4j
public class RequestJWTFilter extends OncePerRequestFilter {
private final UserRepository userRepository;
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// 1. Extraer de la cabecera Authorization de la request
String bearerToken = request.getHeader("Authorization");
if (!StringUtils.hasLength(bearerToken) || !bearerToken.startsWith("Bearer")) {
filterChain.doFilter(request, response);
return;
}
String token = bearerToken.substring("Bearer ".length());
// 2. Verificar el token JWT
Optional<User> userOptional = validateTokenAndExtractUser(token);
if (userOptional.isEmpty()) {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return;
}
// 3. Cargar usuario en contexto de seguridad Spring
User user = userOptional.get();
SimpleGrantedAuthority role = new SimpleGrantedAuthority(user.getRole().toString());
Authentication auth = new UsernamePasswordAuthenticationToken(user, null, List.of(role));
SecurityContextHolder.getContext().setAuthentication(auth);
filterChain.doFilter(request, response);
}
private Optional<User> validateTokenAndExtractUser(String token) {
byte[] key = Base64.getDecoder().decode("FZD5maIaX04mYCwsgckoBh1NJp6T3t62h2MVyEtdo3w=");
try {
String userId = Jwts.parser()
.verifyWith(Keys.hmacShaKeyFor(key))
.build()
.parseSignedClaims(token)
.getPayload()
.getSubject();
return this.userRepository.findById(Long.valueOf(userId));
} catch (JwtException e) {
log.error("Error en la validación del token JWT");
return Optional.empty();
}
}
}
Crear clase SecurityConfig
Esta clase configura la seguridad globalmente:
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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;
@AllArgsConstructor
@Configuration
public class SecurityConfig {
private final RequestJWTFilter jwtFilter;
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// Para versiones >= 6.1 de Spring Security
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf.disable())
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
.requestMatchers("/users/login").permitAll()
.requestMatchers("/users/register").permitAll()
.requestMatchers("files/**").permitAll()
.requestMatchers(HttpMethod.POST, "books").hasAnyAuthority("ADMIN")
.requestMatchers(HttpMethod.PUT, "books").hasAnyAuthority("ADMIN")
.requestMatchers(HttpMethod.DELETE, "books").hasAnyAuthority("ADMIN")
.anyRequest().authenticated()
).addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
.build();
}
Crear clase SecurityUtils
Con esta clase creamos un método getCurrentUser() o getAuthenticatedUser() que devuelva el usuario autenticado guardado en el contexto de seguridad:
import com.certidevs.model.User;
import org.springframework.security.core.context.SecurityContextHolder;
import java.util.Optional;
public class SecurityUtils {
private SecurityUtils() {}
/**
* Devuelve el usuario autenticado extraído de Spring Security
*
* Se utiliza así:
*
* User user = SecurityUtils.getCurrentUser().orElseThrow();
* @return
*/
public static Optional<User> getCurrentUser() {
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof User user) {
return Optional.of(user);
} else {
return Optional.empty();
}
}
public static boolean isAdminCurrentUser() {
if (getCurrentUser().isEmpty()) {
return false;
}
User user = getCurrentUser().get();
return user.getRole().equals(Role.ADMIN);
}
}
Esta clase permite obtener el usuario en cualquier lugar de la aplicación backend, como por ejemplo controladores o servicios.
En este ejemplo se utiliza para obtener el usuario autenticado y asignarlo a una reserva en un controlador:
Clase ReservationController:
@CrossOrigin("*") // Permitir acceso desde cualquier dominio desde el exterior
@RestController
@AllArgsConstructor
@Slf4j
public class ReservationController {
private final ReservationRepository repo;
@PostMapping("reservations")
public Reservation create(@RequestBody Reservation reservation){
SecurityUtils.getCurrentUser().ifPresent(user -> reservation.setUser(user));
return this.repo.save(reservation);
}
}
Otro ejemplo sería actualizar la propia cuenta del usuario desde el frontend:
@PutMapping("users/account")
public User update(@RequestBody User user) {
// Si está autenticado, y el usuario autenticado es ADMIN o es el mismo usuario que la variable user
// entonces actualizar, en caso contrario no actualizamos
SecurityUtils.getCurrentUser().ifPresent(currentUser -> {
if (currentUser.getRole() == Role.ADMIN || Objects.equals(currentUser.getId(), user.getId())) {
this.userRepository.save(user);
} else {
throw new RuntimeException("No puede actualizar"); // Reemplazar por Excepción personalizada
}
});
return user;
}

Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, SpringBoot es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de SpringBoot
Explora más contenido relacionado con SpringBoot y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
- Crear un filtro que intercepte las peticiones HTTP
- Verificar el token JWT de las peticiones
- Identificar el usuario a partir del token JWT
- Cargar el usuario en el contexto de seguridad de Spring Security