█████╗ ██╗ ██╗████████╗██╗ ██╗
██╔══██╗██║ ██║╚══██╔══╝██║ ██║
███████║██║ ██║ ██║ ███████║
██╔══██║██║ ██║ ██║ ██╔══██║
██║ ██║╚██████╔╝ ██║ ██║ ██║
╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝
██████╗ ██████╗ ██████╗ ██████╗ ███████╗ ███████╗██╗ ██╗███████╗████████╗███████╗███╗ ███╗
██╔═══██╗██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔════╝╚██╗ ██╔╝██╔════╝╚══██╔══╝██╔════╝████╗ ████║
██║ ██║██║ ██║ ██║██████╔╝█████╗ ███████╗ ╚████╔╝ ███████╗ ██║ █████╗ ██╔████╔██║
██║▄▄ ██║██║ ██║ ██║██╔══██╗██╔══╝ ╚════██║ ╚██╔╝ ╚════██║ ██║ ██╔══╝ ██║╚██╔╝██║
╚██████╔╝╚██████╗╚██████╔╝██║ ██║███████╗ ███████║ ██║ ███████║ ██║ ███████╗██║ ╚═╝ ██║
╚══▀▀═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚══════╝ ╚═╝ ╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
AUTH SERVICE — SECURITY CORE
Sistema de autenticacion y autorizacion basado en Spring Security y JWT. Implementa un flujo stateless para arquitecturas de microservicios.
- JWT Token — Token stateless con HMAC-SHA256 y expiracion configurable
- BCrypt — Password hashing con salt aleatorio (hashing intencionalmente lento)
- RBAC — Control de acceso por roles (USER / ADMIN) con
@PreAuthorize - 46 Tests — Cobertura unit, integration y slice
- Stateless — Sin sesiones HTTP, ideal para microservicios
- API Docs — Swagger/OpenAPI habilitado en perfil dev
La documentacion interactiva esta disponible en
/swagger-ui.htmltras iniciar el proyecto.
Flujo de autenticacion
1. POST /register → Crear cuenta (sin token)
2. POST /login → Obtener token JWT
3. POST /validate → Verificar token (header: Authorization: Bearer <token>)
4. GET /users → Acceder con token de usuario ADMIN
Diagramas detallados en docs/ARQUITECTURA.md
src/main/java/dev/qcore/auth/
│
├── config/
│ ├── SecurityConfig.java ← Configuracion de seguridad
│ ├── JwtAuthFilter.java ← Filtro JWT (8 pasos)
│ ├── CustomUserDetailsService.java ← Puente BD → Spring Security
│ └── OpenApiConfig.java ← Swagger
│
├── controller/
│ ├── AuthApi.java ← Interface + anotaciones Swagger
│ └── impl/
│ └── AuthApiController.java ← REST controller (thin)
│
├── service/
│ ├── JwtService.java ← Interface JWT
│ ├── AuthService.java ← Interface Auth
│ └── impl/
│ ├── JwtServiceImpl.java ← HMAC-SHA JWT
│ └── AuthServiceImpl.java ← Login, registro, RBAC
│
├── repository/
│ └── UserEntityRepository.java ← Spring Data JPA
│
└── common/
├── constants/ ← ApiPaths, JwtConstants, ErrorCodes
├── enums/
│ └── UserRole.java ← USER, ADMIN
├── model/
│ ├── entities/
│ │ └── UserEntity.java ← JPA + UserDetails
│ ├── dto/
│ │ ├── request/ ← LoginRequest, RegisterRequest
│ │ └── response/ ← TokenResponse, ErrorResponse, etc.
│ └── mapper/
│ └── UserMapper.java ← MapStruct
└── exception/
├── GlobalExceptionHandler.java
├── DuplicateEmailException.java
├── TokenInvalidException.java
├── InvalidCredentialsException.java
└── AccessDeniedException.java
# 1. Clonar el repositorio
git clone https://github.com/user/auth.git
cd auth
# 2. Configurar variables de entorno
export JWT_SECRET="tu-secreto-aqui-minimo-32-caracteres"
export DB_HOST=localhost
export DB_NAME=auth_db
export DB_USERNAME=postgres
export DB_PASSWORD=tu-password
# 3. Ejecutar
./mvnw spring-boot:run
# 4. Abrir Swagger
open http://localhost:8080/swagger-ui.htmlRequisitos previos
- Java 21+
- Maven 3.9+ (o usar el wrapper
./mvnw) - PostgreSQL 16+ corriendo
Que pasa despues de levantar la app
La app arranca en el puerto 8080 por defecto.
Endpoints disponibles:
| Endpoint | Metodo | Descripcion | Autenticacion |
|---|---|---|---|
/api/v1/auth/login |
POST | Login con email/password | Publico |
/api/v1/auth/register |
POST | Registro de usuario | Publico |
/api/v1/auth/whoami |
GET | Informacion del usuario actual | JWT |
/api/v1/admin/dashboard |
GET | Panel de administracion | ADMIN |
Swagger UI: http://localhost:8080/swagger-ui.html
OpenAPI JSON: http://localhost:8080/v3/api-docs
Ejemplos curl
1. Registrar usuario:
curl -X POST http://localhost:8080/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{
"name": "Juan Perez",
"email": "juan@example.com",
"password": "password123"
}'Respuesta:
{
"id": "uuid-aqui",
"name": "Juan Perez",
"email": "juan@example.com",
"roles": ["USER"]
}2. Login:
curl -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "juan@example.com",
"password": "password123"
}'Respuesta:
{
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"tokenType": "Bearer",
"expiresIn": 3600
}3. Usar el token (whoami):
curl -X GET http://localhost:8080/api/v1/auth/whoami \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."4. Acceder a endpoint ADMIN (requiere rol ADMIN):
curl -X GET http://localhost:8080/api/v1/admin/dashboard \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiJ9..."Si el usuario no tiene rol
ADMIN, recibes 403 Forbidden.
Troubleshooting
Error: Puerto 8080 ocupado
Web server failed to start. Port 8080 was already in use.
Solucion: Cambiar el puerto en application.yaml o encontrar el proceso que lo usa:
lsof -i :8080
kill -9 <PID>Error: No se puede conectar a la base de datos
Connection to localhost:5432 refused
Solucion: Verificar que PostgreSQL esta corriendo:
# Linux/Mac
sudo systemctl status postgresql
# Docker
docker ps | grep postgresError: JWT invalido o expirado
Token invalid or expired
Solucion: Generar un nuevo token haciendo login nuevamente. Los tokens duran 1 hora por defecto.
Error: 403 Forbidden
Access Denied
Solucion: El usuario no tiene el rol necesario. Verificar los roles en la BD o registrar un nuevo usuario con rol ADMIN.
./mvnw test| Tipo | Tests | Archivos | Descripcion |
|---|---|---|---|
| 41 | 7 clases | Servicios, filtros, exceptions, controller | |
| 3 | 2 clases | Contexto Spring, seguridad con @WithMockUser | |
| 3 | 1 clase | Repositorio JPA con H2 |
Archivos de test
| Archivo | Tests | Tipo |
|---|---|---|
JwtServiceImplTest.java |
11 | Unit |
JwtAuthFilterTest.java |
8 | Unit |
AuthServiceImplTest.java |
6 | Unit |
GlobalExceptionHandlerTest.java |
6 | Unit |
AuthApiControllerTest.java |
5 | Unit |
CustomUserDetailsServiceTest.java |
2 | Unit |
UserEntityTest.java |
2 | Unit |
AuthServiceMethodSecurityTest.java |
2 | Integration |
AuthApplicationTests.java |
1 | Integration |
UserEntityRepositoryTest.java |
3 | Slice |
El proyecto incluye tutoriales para associates que estan aprendiendo Spring Security:
|
Filter chain, JWT vs sesiones
|
UserEntity, BCrypt, DTOs
|
SecurityConfig, endpoints publicos
|
|
Los 8 pasos del filtro
|
Login, registro, errores
|
@PreAuthorize, 401 vs 403
|
Lo que acabas de ver son las bases. Aqui tienes los proximos niveles.
- AdminInitializer: crear admin al iniciar si no existe (
CommandLineRunner) - Regla: solo admins pueden hacer CRUD a otros USER
- User Mgmt: endpoint para listar/eliminar usuarios
- Logout: endpoint
POST /logout+ blacklist de tokens - Blacklist: tabla
token_blacklist+ filtro la consulta enJwtAuthFilter - Refresh Tokens: access + refresh token pair, endpoint
/refresh
- Password Reset: forgot-password → email con token temporal
- Email Verify: verificacion al registrarse (codigo o link)
- Rate Limiting: prevenir abuso de endpoints
Variables de entorno requeridas
| Variable | Perfil | Descripcion | Ejemplo |
|---|---|---|---|
JWT_SECRET |
Todos | Clave HMAC-SHA (min 32 chars) | mi-secreto-super-seguro-aqui-32ch |
JWT_ACCESS_EXPIRATION |
Todos | Tiempo de vida del token (ms) | 3600000 (1 hora) |
DB_HOST |
Todos | Host de PostgreSQL | localhost |
DB_PORT |
Todos | Puerto de PostgreSQL | 5432 |
DB_NAME |
Todos | Nombre de la BD | auth_db |
DB_USERNAME |
Todos | Usuario de PostgreSQL | postgres |
DB_PASSWORD |
Todos | Password de PostgreSQL | mi-password |
ACTIVE_PROFILE |
Todos | Perfil activo | dev |
NUNCA commitear secrets al repositorio.
Perfiles de configuracion
| Perfil | Swagger | JWT Secret | Base de datos |
|---|---|---|---|
dev |
Habilitado | Hardcoded (inseguro) | PostgreSQL local |
stg |
Deshabilitado | Env var obligatoria | PostgreSQL remoto |
prod |
Deshabilitado | Env var obligatoria | PostgreSQL remoto |
Hecho con Spring Security + JWT