08 - Login con JWT

La tienda ya vende con transacciones; le falta saber quién compra y quién administra. Este hito aplica el tema 06 — Login con JWT: el módulo users con registro y login, el par de tokens — access JWT de vida corta y refresh opaco persistido — y la validación stateless en un filtro propio. La autorización declarativa llega en el hito siguiente.

Pieza Cambio en este hito
Contrato v1.0.0: registro y login; el pedido toma la identidad del token
Estructura Módulo users nuevo, que nace ya con JPA
Mecanismo Access JWT + refresh opaco con rotación; filtro Servlet propio
  1. Ampliar openapi.yaml a v1.0.0 — cambio breaking, por eso salta de versión mayor:
Operación Petición Respuestas
Registro POST /api/users · UserRequest 201 · 400 · 409
Login POST /api/auth/login · LoginRequest 200 · 401
Refresh POST /api/auth/refresh · RefreshToken 200 · 401
Logout POST /api/auth/logout · RefreshToken 204 · 401
Mis pedidos GET /api/me/orders 200 · 401
  1. Registro: name, email y password (mínimo 8). Login y refresh devuelven TokenResponse: accessToken, refreshToken y expiresIn. El logout revoca el refresh que recibe.
  2. El cambio breaking: OrderRequest pierde userId — la identidad del comprador ya no viaja en el cuerpo, sino en el token. Los pedidos de prueba de los hitos anteriores se limpian: su userId era provisional.
  • Módulo nuevo en la verificación, con la estructura aprendida en el hito 07: interfaces de repositorio en domain y mecanismo JPA en persistence.
  • Tablas users y refresh_tokens de la especificación: ampliar el init.sql y reiniciar el volumen de MariaDB. La semilla trae un usuario ADMIN con su hash; el registro crea siempre USER.
  • Contraseñas hash, nunca en claro. El dominio no conoce el mecanismo de hashing: define lo que necesita y la composición aporta la implementación, de la forma que decida cada quien; en el hito 09 pasará a la maquinaria de Spring Security sin tocar el dominio.
  • El login devuelve el par de tokens: access JWT de vida corta, firmado en el servidor, y refresh opaco — una cadena aleatoria sin significado, persistida por su hash con su caducidad y su capacidad de revocación. El refresh se usa una vez: al usarlo se revoca y se entrega uno nuevo (rotación); el logout revoca el refresh recibido. Los claims del access token bastan para que el resto de la aplicación reconozca la identidad — id y rol — sin conocer nada del módulo users.
El access token es stateless, pero el par no: el refresh rota al usarse y se revoca en el logout. La tabla refresh_tokens de la especificación existe por eso.
  • Aún sin Spring Security, la protección es un filtro Servlet propio, como en el tema 06: extrae el token de la cabecera, valida firma y caducidad, deja la identidad a la vista de los controladores y responde 401 en las rutas que la exigen. Los filtros no pasan por los manejadores de la presentación: el 401 lo escribe el filtro con el formato del contrato.
  • Las rutas protegidas son todavía una lista gestionada a mano en el filtro.
  • Con la identidad en marcha, GET /api/me/orders devuelve solo los pedidos del comprador: la identidad viene del token, no de un parámetro.
La lista de rutas «a mano» está a propósito: muestra el límite del enfoque. En el hito 09 la sustituye la cadena declarativa de Spring Security, sin cambiar el flujo de tokens.
  • Registro: usuario correcto → 201; email repetido → 409; contraseña corta → 400.
  • Login: credenciales correctas → 200 con el par de tokens; usuario o contraseña incorrectos → 401.
  • Refresh: correcto → par nuevo y el viejo revocado; reusar el viejo → 401; refresh caducado → 401.
  • Logout: revoca el refresh; usarlo después → 401.
  • Filtro: ruta protegida sin token → 401; con token caducado o mal firmado → 401; rutas públicas sin token pasan; la identidad llega al controlador con id y rol.
  • Mis pedidos: devuelve solo los del comprador del token.
  • El flujo completo corre de punta a punta: registro → login → pedido → mis pedidos → cancelación → refresh → logout.
  • El contrato está en v1.0.0 y el Swagger refleja las operaciones nuevas; OrderRequest ya no lleva userId.
  • La verificación de límites sigue verde con el tercer módulo.
  • Commit y tag hito-08.

Siguiente hito: hito 09 — Integración con Spring Security.

  • clase/daw/dws/1eval/practicas/p8.txt
  • Última modificación: 2026/10/28 13:33
  • por cesguiro