CodeGym /Cursos /Módulo 5. Spring /Resumen de las lecciones anteriores

Resumen de las lecciones anteriores

Módulo 5. Spring
Nivel 18 , Lección 4
Disponible

Hoy vamos a hacer una implementación práctica de JWT en nuestra aplicación. Añadiremos la generación y validación de tokens, configuraremos filtros de seguridad y protegeremos el REST API. Para ello crearemos una pequeña aplicación Spring Boot donde la autenticación se hará mediante JWT.

JWT no es solo una palabra de moda. Es un mecanismo cómodo, ligero y muy usado para autenticación en aplicaciones web modernas. Por ejemplo, si eres desarrollador y vas a una entrevista, te van a preguntar: "cuéntame sobre JWT". Y si no (suerte tuya), en la vida real te lo vas a encontrar en cualquier proyecto que tenga autenticación.


Plan de nuestra aplicación

Nuestra aplicación tendrá dos funcionalidades principales:

  1. Autenticación — el usuario envía sus credenciales (username y password), y el servidor devuelve un JWT.
  2. Acceso a recursos protegidos — el usuario pasa el JWT en el header de las peticiones, y si el token es válido, el servidor da acceso al recurso.

Paso 1. Preparación: configuración del proyecto

Primero vamos a crear un proyecto Spring Boot. Usaremos:

  • Maven para gestionar dependencias.
  • Spring Boot versión 2.7+.
  • Dependencias: spring-boot-starter-security, spring-boot-starter-web, jjwt (Java JWT library).

Configuración de Maven Crea el proyecto con Spring Initializr (https://start.spring.io/). Añade las siguientes dependencias en 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>

  <!-- Biblioteca 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>

Paso 2. Configuración de SecurityConfig

Necesitamos configurar la seguridad y quitar el formulario de login por defecto de Spring Security. Lo sustituiremos por autenticación mediante JWT.

Crea la clase 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() // Acceso a estas rutas sin autorización
            .anyRequest().authenticated()          // El resto de rutas requieren autorización
            .and()
            .httpBasic();

        return http.build();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

Hemos deshabilitado CSRF (solo para mantener el ejemplo sencillo), configurado acceso a los endpoints /api/auth/** sin autorización y exigido autorización para todas las demás peticiones.


Paso 3. Crear la entidad usuario

Necesitamos crear una entidad User para simular el proceso de autenticación. Para simplificar, guardaremos los usuarios en memoria.


public class User {
    private String username;
    private String password;

    // Constructores, getters y setters
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}

Paso 4. Crear el servicio para generar JWT

Ahora implementemos el servicio que generará el 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"; // Nunca guardes secretos en el código en proyectos reales

    public String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 30)) // 30 minutos
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }
}

Paso 5. Controlador para autenticación

Crearemos un controlador que maneje las peticiones de autenticación.


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) {
        // Ejemplo: usuario estático para pruebas
        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("Credenciales incorrectas");
    }
}

Este endpoint recibe username y password, los compara con los datos del usuario (aquí codificados en memoria), y si todo es válido, devuelve el JWT.


Paso 6. Validación del JWT

Ahora añadiremos un filtro que se ejecutará en cada petición comprobando el token recibido.


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); // Quitamos "Bearer "

            try {
                Claims claims = Jwts.parser()
                        .setSigningKey(SECRET_KEY)
                        .parseClaimsJws(token)
                        .getBody();

                String username = claims.getSubject();

                // Establecemos la autenticación
                UsernamePasswordAuthenticationToken auth =
                        new UsernamePasswordAuthenticationToken(username, null, null);
                SecurityContextHolder.getContext().setAuthentication(auth);
            } catch (Exception e) {
                System.out.println("Token inválido.");
            }
        }

        filterChain.doFilter(request, response);
    }
}

No olvides registrar este filtro en la configuración.


@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();
}

Paso 7. Pruebas

Ahora podemos probar la aplicación:

  1. Envía un POST a /api/auth/login con JSON:
    
    {
     "username": "admin",
     "password": "password"
    }
    

    Recibirás el JWT en la respuesta.

  2. Usa ese JWT en el header Authorization para cualquier ruta protegida:
    
    Authorization: Bearer <tu-token>
    

¡Y eso es todo! Hemos protegido nuestro REST API con JWT. En la vida real tendrás que añadir muchas mejoras: almacenar usuarios en una base de datos, gestionar refresh tokens y mucho más. Pero ahora conoces los principios básicos.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION