Una aplicación de demostración de concepto de Spring Boot con WebFlux que implementa autenticación basada en OTP (One-Time Password) utilizando Spring Session con Redis para el manejo de sesiones. Este proyecto está diseñado para mostrar la integración de tecnologías reactivas con sistemas de autenticación modernos.
⚠️ Nota: Este es un proyecto de demostración que requiere mejoras y control de errores robusto para uso en producción.
- Spring Boot 3.5.4 con WebFlux (programación reactiva)
- Spring Security con autenticación personalizada por OTP
- Spring Session con Redis para almacenamiento de sesiones
- Spring Actuator para monitoreo y métricas
- Lombok para reducir código boilerplate
- Manejo de errores global con respuestas estructuradas
- Configuración de seguridad personalizada con filtros reactivos
- AuthController: Maneja el flujo de autenticación (login, validación OTP, logout)
- SessionController: Proporciona endpoints para gestionar sesiones
- DemoController: Endpoint protegido de demostración
- OtpService: Genera y valida códigos OTP
- OTPAuthenticationWebFilter: Filtro personalizado de autenticación
- SecurityConfig: Configuración de seguridad reactiva
- SessionConfig: Configuración de Spring Session con Redis
- Login: Usuario proporciona documento y email
- Generación OTP: Se genera un código de 6 dígitos válido por 5 minutos
- Envío OTP: En un escenario real, el OTP se enviaría por email/SMS al usuario
- Validación: Usuario ingresa el OTP recibido para autenticarse
- Acceso: Usuario autenticado puede acceder a endpoints protegidos
- Logout: Invalidación de sesión
En un entorno de producción, el flujo de OTP funcionaría así:
- Email/SMS: El OTP se enviaría automáticamente al email o teléfono del usuario
- Seguridad: El OTP no se retornaría en la respuesta del endpoint (solo se mostraría en logs para esta demo)
- Expiración: Los códigos expiran automáticamente después de 5 minutos
- Intento único: Cada OTP es válido para un solo uso
sequenceDiagram
participant U as Usuario
participant A as AuthController
participant O as OtpService
participant S as Spring Session
participant R as Redis
participant F as OTPAuthenticationWebFilter
participant D as DemoController
Note over U,D: Flujo de Autenticación OTP
U->>A: POST /auth/login<br/>(documentNumber, email)
A->>S: Crear/Actualizar sesión
S->>R: Almacenar datos en Redis
A->>O: Generar OTP
O-->>O: Almacenar OTP en memoria<br/>(5 min TTL)
A-->>U: OTP generado<br/>(En demo: se muestra en logs)
Note over U: Usuario recibe OTP por email/SMS<br/>(en escenario real)
U->>A: POST /auth/validate?otp=123456
A->>F: Filtro intercepta request
F->>S: Obtener sesión
S->>R: Recuperar datos de Redis
F->>O: Validar OTP
O-->>F: OTP válido/expirado
alt OTP Válido
F->>S: Marcar como autenticado
S->>R: Actualizar sesión en Redis
F-->>A: Autenticación exitosa
A-->>U: 204 No Content
else OTP Inválido/Expirado
F-->>U: 401 Unauthorized
end
Note over U,D: Acceso a Endpoint Protegido
U->>D: GET /api/hello
D->>F: Filtro verifica autenticación
F->>S: Verificar sesión autenticada
S->>R: Recuperar estado de sesión
alt Usuario Autenticado
F-->>D: Permitir acceso
D-->>U: Hello World
else Usuario No Autenticado
F-->>U: 401 Unauthorized
end
Note over U,D: Logout
U->>A: POST /auth/logout
A->>S: Invalidar sesión
S->>R: Eliminar sesión de Redis
A-->>U: Sesión cerrada
graph TB
subgraph "Cliente"
U[Usuario/Cliente]
end
subgraph "Spring WebFlux Application"
A[AuthController]
D[DemoController]
SC[SessionController]
subgraph "Seguridad"
F[OTPAuthenticationWebFilter]
SEC[SecurityConfig]
end
subgraph "Servicios"
O[OtpService]
end
subgraph "Configuración"
SESS[SessionConfig]
end
end
subgraph "Almacenamiento"
R[(Redis)]
M[Memoria ConcurrentHashMap<br/>OTP Storage]
end
subgraph "Spring Session"
WS[WebSession]
SR[SessionRepository]
end
U -->|HTTP Requests| A
U -->|HTTP Requests| D
U -->|HTTP Requests| SC
A --> O
A --> WS
D --> F
SC --> WS
F --> O
F --> WS
F --> SEC
WS --> SR
SR --> R
O --> M
SESS --> WS
SESS --> SR
classDef controller fill:#e1f5fe
classDef security fill:#fff3e0
classDef service fill:#f3e5f5
classDef storage fill:#e8f5e8
classDef session fill:#fce4ec
class A,D,SC controller
class F,SEC security
class O service
class R,M storage
class WS,SR,SESS session
Tecnología | Versión | Propósito |
---|---|---|
Java | 17 | Lenguaje de programación |
Spring Boot | 3.5.4 | Framework principal |
Spring WebFlux | 3.5.4 | Programación reactiva |
Spring Security | 3.5.4 | Seguridad |
Spring Session | 3.5.4 | Gestión de sesiones |
Redis | - | Almacenamiento de sesiones |
Lombok | - | Reducción de código |
Maven | - | Gestión de dependencias |
- Java 17 o superior
- Maven 3.6+
- Redis Server (puerto 6379)
git clone <repository-url>
cd spring-web-session-with-redis
Asegúrate de que Redis esté ejecutándose en localhost:6379
.
Opción 1 - Docker (Recomendado):
docker run -d -p 6379:6379 redis:alpine
Opción 2 - Instalación local:
- Ubuntu/Debian:
sudo apt install redis-server
- macOS:
brew install redis
- Windows: Descargar desde redis.io
Verificar que Redis esté funcionando:
redis-cli ping
# Debe responder: PONG
mvn spring-boot:run
La aplicación estará disponible en http://localhost:8080
Método | Endpoint | Descripción | Autenticación |
---|---|---|---|
POST | /auth/login |
Genera OTP para documento y email | No |
POST | /auth/validate |
Valida OTP y autentica usuario | No (pero requiere OTP válido) |
POST | /auth/status |
Consulta estado de autenticación | No |
POST | /auth/logout |
Cierra sesión | No |
Método | Endpoint | Descripción | Autenticación |
---|---|---|---|
GET | /api/session/info |
Información de la sesión actual | No |
POST | /api/session/set |
Establece atributo en sesión | No |
GET | /api/session/get |
Obtiene atributo de sesión | No |
POST | /api/session/remove |
Elimina atributo de sesión | No |
POST | /api/session/invalidate |
Invalida sesión actual | No |
Método | Endpoint | Descripción | Autenticación |
---|---|---|---|
GET | /api/hello |
Endpoint protegido de demostración | Sí |
Método | Endpoint | Descripción | Autenticación |
---|---|---|---|
GET | /actuator/health |
Estado de salud de la aplicación | No |
GET | /actuator/info |
Información de la aplicación | No |
GET | /actuator/metrics |
Métricas de la aplicación | No |
server:
port: 8080
spring:
application:
name: spring-web-session-with-redis
data:
redis:
host: localhost
port: 6379
database: 0
timeout: 2000ms
session:
store-type: redis
redis:
namespace: "spring:demo:session"
flush-mode: on_save
timeout: 30m
management:
endpoints:
web:
exposure:
include: health,info,metrics
endpoint:
health:
show-details: always
Puedes sobrescribir la configuración usando variables de entorno:
export SPRING_DATA_REDIS_HOST=localhost
export SPRING_DATA_REDIS_PORT=6379
export SERVER_PORT=8080
- Login:
curl -X POST "http://localhost:8080/auth/login?documentNumber=12345678&email=test@example.com"
- Validar OTP (usar el OTP devuelto en el paso anterior):
curl -X POST "http://localhost:8080/auth/validate?otp=123456" \
-H "Cookie: SESSION=your-session-id"
- Acceder a endpoint protegido:
curl -X GET "http://localhost:8080/api/hello" \
-H "Cookie: SESSION=your-session-id"
- Verificar estado:
curl -X POST "http://localhost:8080/auth/status" \
-H "Cookie: SESSION=your-session-id"
- Logout:
curl -X POST "http://localhost:8080/auth/logout" \
-H "Cookie: SESSION=your-session-id"
El proyecto incluye archivos .http
para pruebas:
test-auth-flow.http
: Flujo completo de autenticacióntest-endpoints.http
: Pruebas de endpointstest-session.http
: Pruebas de funcionalidad de sesiones
- CSRF: Deshabilitado para APIs REST
- Autenticación: Basada en OTP con Spring Session
- Autorización: Endpoints protegidos requieren autenticación
- Sesiones: Almacenadas en Redis con namespace personalizado
/api/hello
: Requiere autenticación con@PreAuthorize("hasRole('USER')")
/auth/validate
: Validación de OTP- Cualquier endpoint bajo
/api/protected/**
/actuator/**
: Monitoreo y métricas/auth/login
,/auth/status
,/auth/logout
: Gestión de autenticación/api/session/**
: Gestión de sesiones
- Health:
/actuator/health
- Estado de salud de la aplicación - Info:
/actuator/info
- Información de la aplicación - Metrics:
/actuator/metrics
- Métricas de rendimiento
La aplicación configura logging estructurado:
logging:
level:
com.example.springwebsession: INFO
org.springframework.web: DEBUG
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
src/main/java/com/example/springwebsession/
├── config/
│ └── SessionConfig.java # Configuración de Spring Session
├── controller/
│ ├── AuthController.java # Controlador de autenticación
│ ├── DemoController.java # Controlador de demostración
│ └── SessionController.java # Controlador de sesiones
├── exception/
│ └── GlobalExceptionHandler.java # Manejador global de excepciones
├── security/
│ ├── OTPAuthenticationWebFilter.java # Filtro de autenticación personalizado
│ └── SecurityConfig.java # Configuración de seguridad
├── service/
│ └── OtpService.java # Servicio de OTP
└── SpringWebSessionApplication.java # Clase principal
mvn clean compile
mvn clean package
java -jar target/spring-web-session-with-redis-1.0.0.jar
Puedes usar diferentes perfiles para diferentes entornos:
mvn spring-boot:run -Dspring-boot.run.profiles=dev
mvn spring-boot:run -Dspring-boot.run.profiles=prod
Si Redis no está disponible:
Error: Unable to connect to Redis
Solución: Verificar que Redis esté ejecutándose en localhost:6379
Si las sesiones no persisten:
Error: No active session
Solución: Verificar configuración de Redis y namespace de sesión
Si el OTP expira rápidamente:
Error: Invalid or expired OTP
Solución: Los OTP expiran en 5 minutos. Generar uno nuevo con /auth/login
- OTP en memoria: Los códigos se almacenan en ConcurrentHashMap (no persistente)
- Sin envío real: Los OTP se muestran en logs, no se envían por email/SMS
- Control de errores básico: Manejo de excepciones simplificado
- Sin rate limiting: No hay protección contra ataques de fuerza bruta
- Logs de seguridad: Información sensible visible en logs
- Almacenamiento persistente: Mover OTP a Redis con TTL automático
- Servicio de notificaciones: Integrar con proveedores de email/SMS
- Rate limiting: Implementar límites de intentos por IP/usuario
- Auditoría: Logging detallado de eventos de seguridad
- Validación robusta: Validación de entrada más estricta
- Configuración segura: Variables de entorno para datos sensibles
- Las sesiones se configuran con timeout de 30 minutos
- El filtro de autenticación maneja tanto validación de OTP como verificación de sesión
- Spring Session con Redis permite escalabilidad horizontal
- Manejo reactivo de todas las operaciones
- Fork el proyecto
- Crea una rama para tu feature (
git checkout -b feature/AmazingFeature
) - Commit tus cambios (
git commit -m 'Add some AmazingFeature'
) - Push a la rama (
git push origin feature/AmazingFeature
) - Abre un Pull Request
Este proyecto está bajo la Licencia Apache 2.0. Ver el archivo LICENSE
para más detalles.
- Uso comercial y privado: Puedes usar este código en proyectos comerciales sin restricciones
- Modificación libre: Puedes modificar el código como necesites
- Licencia diferente: Puedes distribuir tu proyecto bajo cualquier licencia
- Solo atribución: Solo necesitas incluir el archivo de licencia original
- Sin copyleft: No estás obligado a usar la misma licencia en tu proyecto
Desarrollado como proyecto de demostración de concepto de Spring Boot con WebFlux y Redis.
Esta es una aplicación de DEMOSTRACIÓN DE CONCEPTO.
-
Seguridad:
- Almacenamiento de OTP en Redis con TTL automático
- Envío real de OTP por email/SMS (no mostrar en logs)
- Rate limiting para endpoints de autenticación
- Validación robusta de entrada de datos
- Logging de auditoría sin información sensible
-
Escalabilidad:
- Configuración de Redis cluster
- Manejo de fallos de Redis
- Monitoreo y alertas
-
Control de Errores:
- Manejo detallado de excepciones
- Respuestas de error estructuradas
- Logging apropiado para producción
-
Configuración:
- Variables de entorno para configuración sensible
- Perfiles de configuración por ambiente
- Configuración de seguridad adicional
- Integración Spring WebFlux con Redis
- Autenticación OTP personalizada
- Manejo de sesiones reactivas
- Arquitectura de filtros de seguridad
- Spring Session con almacenamiento externo