Integrar autenticación Cognito con Floci local y soporte AWS
- Añade servicios y configuración Cognito para login web (admins) y API (scanners) - Soporta Floci cuando AWS_ENDPOINT_URL está definido; AWS real en caso contrario - Incluye bootstrap Floci, docker-compose.local.yml y carga automática en run-local.sh - Documenta arquitectura, flujos y arranque manual/IntelliJ en README - Actualiza gitignore: excluye Flutter, H2 local y artefactos generados
This commit is contained in:
@@ -1,78 +1,362 @@
|
||||
# Base Admin Web
|
||||
|
||||
Proyecto base de Spring Boot con Metronic, autenticacion local y estructura modular lista para reutilizar en nuevos paneles administrativos.
|
||||
Proyecto base de Spring Boot con Metronic, autenticación Cognito (o local), panel administrativo y API de escaneo para la app móvil Flutter.
|
||||
|
||||
Sirve como plantilla reutilizable para nuevos paneles internos: estructura modular, seguridad dual (web + API), módulo de códigos pase con QR y soporte H2/PostgreSQL.
|
||||
|
||||
---
|
||||
|
||||
## Tabla de contenidos
|
||||
|
||||
- [Vista general](#vista-general)
|
||||
- [Stack](#stack)
|
||||
- [Arquitectura del sistema](#arquitectura-del-sistema)
|
||||
- [Módulos Maven](#módulos-maven)
|
||||
- [Seguridad y autenticación](#seguridad-y-autenticación)
|
||||
- [Cognito: local (Floci) vs AWS](#cognito-local-floci-vs-aws)
|
||||
- [Módulo de códigos pase](#módulo-de-códigos-pase)
|
||||
- [API REST](#api-rest)
|
||||
- [App Flutter (escáner)](#app-flutter-escáner)
|
||||
- [Arranque local](#arranque-local)
|
||||
- [Arranque manual e IntelliJ IDEA](#arranque-manual-e-intellij-idea)
|
||||
- [Perfiles Spring](#perfiles-spring)
|
||||
- [Variables de entorno](#variables-de-entorno)
|
||||
- [Objetivo de esta base](#objetivo-de-esta-base)
|
||||
|
||||
---
|
||||
|
||||
## Vista general
|
||||
|
||||
El sistema tiene **dos canales de acceso** con roles distintos:
|
||||
|
||||
| Canal | Usuario | Rol | Autenticación |
|
||||
|-------|---------|-----|---------------|
|
||||
| Panel web (Thymeleaf) | `admin` | `ROLE_ADMIN` | Sesión HTTP (form login) |
|
||||
| API móvil / externa | `scanner` | `ROLE_SCANNER` | JWT Bearer (stateless) |
|
||||
|
||||
Por defecto ambos canales usan **Amazon Cognito** (emulado en local con Floci). Con `APP_COGNITO_ENABLED=false` se usa autenticación local contra la base de datos y JWT firmado con secreto propio.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph clients["Clientes"]
|
||||
Browser["Navegador<br/>Panel admin"]
|
||||
Flutter["App Flutter<br/>QR Scanner"]
|
||||
end
|
||||
|
||||
subgraph app["Spring Boot — base-admin-web-jar"]
|
||||
WebChain["Filter chain web<br/>sesión + CSRF"]
|
||||
ApiChain["Filter chain API<br/>stateless + JWT"]
|
||||
PassCodes["PassCodeService"]
|
||||
DB[("H2 / PostgreSQL")]
|
||||
end
|
||||
|
||||
subgraph auth["Autenticación"]
|
||||
Cognito["Cognito<br/>Floci local o AWS"]
|
||||
LocalAuth["BD local + JWT HS256<br/>APP_COGNITO_ENABLED=false"]
|
||||
end
|
||||
|
||||
Browser -->|"/login, /admin/**"| WebChain
|
||||
Flutter -->|"/api/auth/login<br/>/api/pass-codes/scan"| ApiChain
|
||||
WebChain --> PassCodes
|
||||
ApiChain --> PassCodes
|
||||
PassCodes --> DB
|
||||
WebChain --> Cognito
|
||||
ApiChain --> Cognito
|
||||
WebChain -.-> LocalAuth
|
||||
ApiChain -.-> LocalAuth
|
||||
```
|
||||
|
||||
**URLs locales:**
|
||||
|
||||
| Recurso | URL |
|
||||
|---------|-----|
|
||||
| Login | http://localhost:8080/login |
|
||||
| Dashboard | http://localhost:8080/dashboard |
|
||||
| Códigos pase | http://localhost:8080/admin/pass-codes |
|
||||
| H2 Console | http://localhost:8080/h2-console |
|
||||
| Floci (Cognito local) | http://localhost:4566 |
|
||||
|
||||
---
|
||||
|
||||
## Stack
|
||||
|
||||
- Spring Boot 4.0.6
|
||||
- Java 25
|
||||
- Spring Security
|
||||
- Thymeleaf
|
||||
- JPA / Hibernate
|
||||
- H2 en archivo para desarrollo rapido
|
||||
- PostgreSQL listo por perfil
|
||||
- JWT para autenticacion API de escaneo
|
||||
- Assets Metronic reutilizados desde `base-web`
|
||||
| Capa | Tecnología |
|
||||
|------|------------|
|
||||
| Backend | Spring Boot 4.0.6, Java 25 |
|
||||
| Seguridad | Spring Security, OAuth2 Resource Server, AWS SDK Cognito |
|
||||
| Vistas | Thymeleaf + Metronic (`base-web`) |
|
||||
| Persistencia | JPA / Hibernate, H2 (dev), PostgreSQL (perfil `postgres`) |
|
||||
| Auth | Cognito (default) o BD local + JWT HS256 |
|
||||
| Móvil | Flutter (`flutter-app/qrscanner`) |
|
||||
| Local AWS | [Floci](https://floci.io/floci/getting-started/quick-start/) (Cognito en puerto 4566) |
|
||||
|
||||
## Estructura
|
||||
---
|
||||
|
||||
- `base-admin-web-domain`: entidades y repositorios
|
||||
- `base-admin-web-application`: servicios y carga inicial
|
||||
- `base-admin-web-configuration`: seguridad, JWT y perfiles
|
||||
- `base-admin-web-web`: controladores y vistas Thymeleaf
|
||||
- `base-admin-web-jar`: punto de entrada ejecutable
|
||||
## Arquitectura del sistema
|
||||
|
||||
## Usuarios iniciales
|
||||
### Capas y dependencias
|
||||
|
||||
### Administrador (panel web)
|
||||
```mermaid
|
||||
flowchart BT
|
||||
jar["base-admin-web-jar<br/>punto de entrada + application.yml"]
|
||||
web["base-admin-web-web<br/>controladores + Thymeleaf"]
|
||||
config["base-admin-web-configuration<br/>seguridad, JWT, Cognito"]
|
||||
app["base-admin-web-application<br/>servicios + carga inicial"]
|
||||
domain["base-admin-web-domain<br/>entidades + repositorios"]
|
||||
|
||||
- Usuario: `admin`
|
||||
- Password: `Admin123*`
|
||||
- Rol: `ROLE_ADMIN`
|
||||
- Acceso: login web en `/login`, dashboard y modulo de codigos pase
|
||||
jar --> web
|
||||
jar --> config
|
||||
jar --> app
|
||||
jar --> domain
|
||||
web --> config
|
||||
web --> app
|
||||
config --> app
|
||||
app --> domain
|
||||
```
|
||||
|
||||
Variables de entorno:
|
||||
| Módulo | Responsabilidad |
|
||||
|--------|-----------------|
|
||||
| `base-admin-web-domain` | Entidades JPA (`AppUser`, `PassCode`, `Role`), repositorios |
|
||||
| `base-admin-web-application` | `PassCodeService`, `AppUserDetailsService`, `InitialDataLoader` |
|
||||
| `base-admin-web-configuration` | `SecurityConfig`, `ApiSecurityConfig`, Cognito, `JwtConfig` |
|
||||
| `base-admin-web-web` | Controladores MVC y API, DTOs, plantillas HTML |
|
||||
| `base-admin-web-jar` | `BaseAdminWebApplication`, perfiles, dependencias runtime (H2, PostgreSQL) |
|
||||
|
||||
- `APP_ADMIN_USERNAME`
|
||||
- `APP_ADMIN_PASSWORD`
|
||||
- `APP_ADMIN_DISPLAY_NAME`
|
||||
### Dos cadenas de seguridad
|
||||
|
||||
### Escaneador (API)
|
||||
Spring Security define **dos filter chains** con distinto comportamiento:
|
||||
|
||||
- Usuario: `scanner`
|
||||
- Password: `Scanner123*`
|
||||
- Rol: `ROLE_SCANNER`
|
||||
- Acceso: solo login por API y escaneo de codigos pase
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph order1["@Order(1) — ApiSecurityConfig"]
|
||||
A1["/api/**"]
|
||||
A2["Stateless"]
|
||||
A3["JWT Bearer"]
|
||||
A4["ROLE_SCANNER en /scan"]
|
||||
end
|
||||
|
||||
Variables de entorno:
|
||||
subgraph order2["@Order(2) — SecurityConfig"]
|
||||
B1["Todo excepto /api/**"]
|
||||
B2["Sesión HTTP"]
|
||||
B3["Form login /login"]
|
||||
B4["ROLE_ADMIN en /admin/**"]
|
||||
end
|
||||
|
||||
- `APP_SCANNER_USERNAME`
|
||||
- `APP_SCANNER_PASSWORD`
|
||||
- `APP_SCANNER_DISPLAY_NAME`
|
||||
A1 --> A2 --> A3 --> A4
|
||||
B1 --> B2 --> B3 --> B4
|
||||
```
|
||||
|
||||
## Modulo de codigos pase
|
||||
| Ruta | Acceso | Mecanismo |
|
||||
|------|--------|-----------|
|
||||
| `/login`, `/assets/**`, `/error`, `/h2-console/**` | Público | — |
|
||||
| `/dashboard`, `/admin/**` | `ROLE_ADMIN` | Sesión web |
|
||||
| `/api/auth/login`, `/api/auth/refresh` | Público | Emite tokens (solo `scanner`) |
|
||||
| `/api/pass-codes/scan` | `ROLE_SCANNER` | JWT Bearer |
|
||||
|
||||
Panel administrativo para dar de alta y consultar codigos pase.
|
||||
---
|
||||
|
||||
- URL: `http://localhost:8080/admin/pass-codes`
|
||||
- Tambien accesible desde el boton **Codigos pase** en el dashboard
|
||||
- Alta por nombre; el sistema genera un codigo aleatorio de 10 caracteres
|
||||
- Vista con tabla de estatus, escaneo y fecha de creacion
|
||||
- Modal Metronic para ver el QR de cada codigo
|
||||
- Al cerrar el modal, la lista se actualiza automaticamente por si el codigo fue utilizado
|
||||
## Seguridad y autenticación
|
||||
|
||||
Campos del codigo pase:
|
||||
### Usuarios iniciales
|
||||
|
||||
- Nombre
|
||||
- Codigo aleatorio
|
||||
- Fecha de creacion
|
||||
- Estatus (`ACTIVO` / `UTILIZADO`)
|
||||
- Indicador de escaneado
|
||||
- Fecha de escaneo
|
||||
#### Administrador (panel web)
|
||||
|
||||
## API
|
||||
- Usuario: `admin` / `Admin123*`
|
||||
- Grupo Cognito: `admins` → `ROLE_ADMIN`
|
||||
- Acceso: login en `/login`, dashboard, módulo de códigos pase
|
||||
- El usuario `scanner` **no** puede entrar al panel web
|
||||
|
||||
### Login escaner
|
||||
#### Escáner (API / Flutter)
|
||||
|
||||
- Usuario: `scanner` / `Scanner123*`
|
||||
- Grupo Cognito: `scanners` → `ROLE_SCANNER`
|
||||
- Acceso: `POST /api/auth/login` y `POST /api/pass-codes/scan`
|
||||
- El usuario `admin` **no** puede autenticarse por API
|
||||
|
||||
### Flujo de login web (panel admin)
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor Admin as Navegador (admin)
|
||||
participant SC as SecurityConfig
|
||||
participant CP as CognitoWebAuthenticationProvider
|
||||
participant CA as CognitoUserAuthenticator
|
||||
participant Cognito as Cognito (Floci/AWS)
|
||||
|
||||
Admin->>SC: POST /login (user, password)
|
||||
SC->>CP: authenticate()
|
||||
CP->>CA: admin client + ROLE_ADMIN
|
||||
CA->>Cognito: InitiateAuth USER_PASSWORD_AUTH
|
||||
Cognito-->>CA: tokens + cognito:groups
|
||||
CA-->>CP: AuthenticatedWebUser (grupo admins)
|
||||
CP-->>SC: sesión HTTP creada
|
||||
SC-->>Admin: redirect /dashboard
|
||||
```
|
||||
|
||||
**Clases clave:** `SecurityConfig`, `CognitoWebSecurityConfig`, `CognitoWebAuthenticationProvider`, `CognitoUserAuthenticator`, `AuthController`
|
||||
|
||||
Con `APP_COGNITO_ENABLED=false`, el flujo usa `DaoAuthenticationProvider` → `AppUserDetailsService` → tabla `users` (BCrypt).
|
||||
|
||||
### Flujo de login y escaneo API
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor App as Flutter (scanner)
|
||||
participant API as AuthApiController
|
||||
participant AS as ApiAuthService
|
||||
participant Cognito as Cognito
|
||||
participant Scan as PassCodeApiController
|
||||
participant Svc as PassCodeService
|
||||
|
||||
App->>API: POST /api/auth/login
|
||||
API->>AS: login()
|
||||
AS->>Cognito: scanner client + ROLE_SCANNER
|
||||
Cognito-->>App: accessToken, refreshToken, idToken
|
||||
|
||||
App->>Scan: POST /api/pass-codes/scan<br/>Authorization: Bearer ...
|
||||
Scan->>Scan: validar JWT (JWK o HS256)
|
||||
Scan->>Svc: scan(code)
|
||||
Svc-->>App: código marcado UTILIZADO
|
||||
```
|
||||
|
||||
**Clases clave:** `ApiSecurityConfig`, `AuthApiController`, `ApiAuthService`, `CognitoAuthService`, `PassCodeApiController`, `JwtConfig`
|
||||
|
||||
### Modo local sin Cognito
|
||||
|
||||
Con `APP_COGNITO_ENABLED=false` (perfil `test` lo usa automáticamente):
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph web["Web"]
|
||||
W1["Form login"] --> W2["AppUser en BD"]
|
||||
end
|
||||
subgraph api["API"]
|
||||
A1["POST /api/auth/login"] --> A2["BD + JwtEncoder HS256"]
|
||||
A3["POST /api/auth/refresh"] --> A4["No disponible"]
|
||||
A5["Bearer token"] --> A6["localJwtDecoder HS256"]
|
||||
end
|
||||
```
|
||||
|
||||
| `APP_COGNITO_ENABLED` | Web | API login | Validación JWT | Refresh |
|
||||
|----------------------|-----|-----------|----------------|---------|
|
||||
| `true` (default) | Cognito client admin | Cognito client scanner | JWK Set URI | Sí |
|
||||
| `false` | BD + BCrypt | BD + JWT local | Secreto HS256 | No |
|
||||
|
||||
Los usuarios se siembran con `InitialDataLoader` usando `APP_ADMIN_*` y `APP_SCANNER_*`.
|
||||
|
||||
---
|
||||
|
||||
## Cognito: local (Floci) vs AWS
|
||||
|
||||
El SDK de Cognito elige el destino según `AWS_ENDPOINT_URL`:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Start["APP_COGNITO_ENABLED=true"]
|
||||
Check{"¿AWS_ENDPOINT_URL<br/>definido?"}
|
||||
Floci["Floci localhost:4566<br/>credenciales test/test"]
|
||||
AWS["Cognito real en AWS<br/>DefaultCredentialsProvider<br/>IAM / perfil / env AWS"]
|
||||
|
||||
Start --> Check
|
||||
Check -->|Sí| Floci
|
||||
Check -->|No| AWS
|
||||
```
|
||||
|
||||
### Local con Floci
|
||||
|
||||
`./run-local.sh` hace todo el setup:
|
||||
|
||||
1. Levanta Floci (y PostgreSQL si el perfil lo incluye)
|
||||
2. Espera el bootstrap de Cognito (hasta 120 s)
|
||||
3. Carga `docker/floci/generated/cognito.local.env` automáticamente
|
||||
|
||||
El script `docker/floci/init/ready.d/10-cognito-bootstrap.sh` crea:
|
||||
|
||||
| Recurso | Valor |
|
||||
|---------|-------|
|
||||
| User Pool ID | `us-east-1_BaseAdminWeb` |
|
||||
| Usuario admin | `admin` / `Admin123*` → grupo `admins` |
|
||||
| Usuario scanner | `scanner` / `Scanner123*` → grupo `scanners` |
|
||||
| App client admin | `base-admin-web-admin` |
|
||||
| App client scanner | `base-admin-web-scanner` |
|
||||
|
||||
Variables generadas en `docker/floci/generated/cognito.local.env` (gitignored).
|
||||
|
||||
**Levantar solo Floci:**
|
||||
|
||||
```bash
|
||||
./scripts/floci-up.sh
|
||||
```
|
||||
|
||||
**Levantar stack Docker completo:**
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.local.yml up -d
|
||||
```
|
||||
|
||||
| Servicio | Puerto | Persistencia |
|
||||
|----------|--------|--------------|
|
||||
| Floci | 4566 | volumen `base_admin_web_floci_data` |
|
||||
| PostgreSQL | 5432 | volumen `base_admin_web_postgres_data` |
|
||||
|
||||
### Producción con AWS Cognito
|
||||
|
||||
**No definir** `AWS_ENDPOINT_URL`. Configura el User Pool real:
|
||||
|
||||
```bash
|
||||
export APP_COGNITO_ENABLED=true
|
||||
export COGNITO_REGION=us-east-1
|
||||
export COGNITO_USER_POOL_ID=us-east-1_XXXXX
|
||||
export COGNITO_ISSUER_URI=https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXX
|
||||
export COGNITO_JWK_SET_URI=https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXX/.well-known/jwks.json
|
||||
export COGNITO_ADMIN_CLIENT_ID=...
|
||||
export COGNITO_SCANNER_CLIENT_ID=...
|
||||
# Credenciales AWS vía IAM role, perfil o AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
|
||||
```
|
||||
|
||||
En AWS replica la misma estructura que Floci: grupos `admins`/`scanners`, clients con `USER_PASSWORD_AUTH` habilitado.
|
||||
|
||||
### Verificar Cognito local
|
||||
|
||||
```bash
|
||||
export AWS_ENDPOINT_URL=http://localhost:4566
|
||||
export AWS_DEFAULT_REGION=us-east-1
|
||||
export AWS_ACCESS_KEY_ID=test
|
||||
export AWS_SECRET_ACCESS_KEY=test
|
||||
|
||||
aws cognito-idp list-user-pools --max-results 10 --endpoint-url $AWS_ENDPOINT_URL
|
||||
aws cognito-idp list-users --user-pool-id us-east-1_BaseAdminWeb --endpoint-url $AWS_ENDPOINT_URL
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Módulo de códigos pase
|
||||
|
||||
Panel para dar de alta y consultar códigos pase con QR.
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> ACTIVO: admin crea código (10 chars)
|
||||
ACTIVO --> UTILIZADO: scanner escanea QR
|
||||
```
|
||||
|
||||
| Campo | Descripción |
|
||||
|-------|-------------|
|
||||
| Nombre | Etiqueta descriptiva |
|
||||
| Código | Aleatorio de 10 caracteres (único) |
|
||||
| Estatus | `ACTIVO` / `UTILIZADO` |
|
||||
| Escaneado | Indicador booleano |
|
||||
| Fechas | Creación y escaneo |
|
||||
|
||||
**Panel web:** `GET /admin/pass-codes` — tabla Metronic, modal QR, refresco automático al cerrar el modal.
|
||||
|
||||
**Entidad:** `PassCode` → **Servicio:** `PassCodeService` → **Controladores:** `PassCodeController` (web), `PassCodeApiController` (API).
|
||||
|
||||
---
|
||||
|
||||
## API REST
|
||||
|
||||
### Login escáner
|
||||
|
||||
```http
|
||||
POST /api/auth/login
|
||||
@@ -84,22 +368,35 @@ Content-Type: application/json
|
||||
}
|
||||
```
|
||||
|
||||
Respuesta exitosa:
|
||||
Respuesta (Cognito):
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"accessToken": "eyJhbG...",
|
||||
"refreshToken": "dXMtZWFzdC0x...",
|
||||
"idToken": "eyJhbG...",
|
||||
"tokenType": "Bearer",
|
||||
"expiresIn": 28800,
|
||||
"expiresIn": 3600,
|
||||
"username": "scanner",
|
||||
"roles": ["ROLE_SCANNER"]
|
||||
}
|
||||
```
|
||||
|
||||
Solo usuarios con rol `SCANNER` pueden iniciar sesion por API. El usuario `admin` no puede autenticarse por este endpoint.
|
||||
### Renovar token
|
||||
|
||||
### Escanear codigo pase
|
||||
```http
|
||||
POST /api/auth/refresh
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"refreshToken": "dXMtZWFzdC0x..."
|
||||
}
|
||||
```
|
||||
|
||||
Solo disponible con Cognito habilitado.
|
||||
|
||||
### Escanear código pase
|
||||
|
||||
```http
|
||||
POST /api/pass-codes/scan
|
||||
@@ -111,19 +408,48 @@ Content-Type: application/json
|
||||
}
|
||||
```
|
||||
|
||||
Marca el codigo como escaneado y cambia su estatus a `UTILIZADO`.
|
||||
|
||||
### Listado para el panel (requiere sesion admin)
|
||||
### Listado JSON (panel admin, requiere sesión)
|
||||
|
||||
```http
|
||||
GET /admin/pass-codes/list
|
||||
```
|
||||
|
||||
Devuelve JSON con los codigos registrados. Se usa internamente para refrescar la tabla del panel.
|
||||
---
|
||||
|
||||
## App Flutter (escáner)
|
||||
|
||||
Ubicación: `flutter-app/qrscanner/`
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Login["LoginScreen"] --> Home["HomeScreen"]
|
||||
Home --> Scanner["ScannerScreen"]
|
||||
Scanner -->|QR detectado| API["POST /api/pass-codes/scan"]
|
||||
Login -->|credenciales| Auth["POST /api/auth/login"]
|
||||
```
|
||||
|
||||
| Archivo | Rol |
|
||||
|---------|-----|
|
||||
| `lib/screens/login_screen.dart` | Login y sesión |
|
||||
| `lib/screens/scanner_screen.dart` | Lectura QR + escaneo |
|
||||
| `lib/services/auth_service.dart` | Login, SharedPreferences |
|
||||
| `lib/services/pass_code_service.dart` | Scan con Bearer token |
|
||||
| `lib/config/api_config.dart` | URL base de la API |
|
||||
|
||||
**Ejecutar apuntando al backend local:**
|
||||
|
||||
```bash
|
||||
cd flutter-app/qrscanner
|
||||
flutter run --dart-define=API_BASE_URL=http://TU_IP:8080
|
||||
```
|
||||
|
||||
La app valida que el usuario tenga `ROLE_SCANNER` (o grupo `scanners`) antes de permitir el escaneo.
|
||||
|
||||
---
|
||||
|
||||
## Arranque local
|
||||
|
||||
Desde la raiz del proyecto:
|
||||
### Opción recomendada
|
||||
|
||||
```bash
|
||||
./run-local.sh
|
||||
@@ -131,44 +457,39 @@ Desde la raiz del proyecto:
|
||||
|
||||
El script:
|
||||
|
||||
- Detecta automaticamente Temurin/Java 25
|
||||
- Compila todos los modulos del proyecto
|
||||
- Arranca el modulo ejecutable `base-admin-web-jar`
|
||||
- Muestra el perfil activo y la base de datos en uso
|
||||
- Detecta Java 25 (Temurin)
|
||||
- Levanta Floci (y PostgreSQL si aplica)
|
||||
- Espera y carga `cognito.local.env`
|
||||
- Compila e inicia `base-admin-web-jar`
|
||||
|
||||
Si `SPRING_PROFILES_ACTIVE` incluye `postgres`, el script intenta levantar automaticamente el contenedor definido en `docker-compose-postgres.local.yml` antes de iniciar la aplicacion.
|
||||
Si ya tienes `AWS_ENDPOINT_URL` exportado, no sobrescribe tus variables (útil para probar contra AWS).
|
||||
|
||||
Tambien puedes ejecutar:
|
||||
### Maven manual (terminal)
|
||||
|
||||
```bash
|
||||
mvn test
|
||||
# 1. Levantar Floci (Cognito local)
|
||||
docker compose -f docker-compose.local.yml up -d floci
|
||||
|
||||
# 2. Esperar el bootstrap (genera cognito.local.env)
|
||||
# Revisa que exista: docker/floci/generated/cognito.local.env
|
||||
|
||||
# 3. Cargar variables Cognito en la shell
|
||||
set -a && source docker/floci/generated/cognito.local.env && set +a
|
||||
|
||||
# 4. Compilar y arrancar
|
||||
mvn -pl base-admin-web-jar -am -DskipTests install
|
||||
mvn -pl base-admin-web-jar spring-boot:run
|
||||
```
|
||||
|
||||
Aplicacion:
|
||||
Si Floci ya estaba levantado de antes, basta con el `source` y el `spring-boot:run`.
|
||||
|
||||
- Login: `http://localhost:8080/login`
|
||||
- Dashboard: `http://localhost:8080/dashboard`
|
||||
- Codigos pase: `http://localhost:8080/admin/pass-codes`
|
||||
- H2 Console: `http://localhost:8080/h2-console`
|
||||
|
||||
## Perfiles
|
||||
|
||||
- `dev`: H2 en archivo (`./data/h2/`), consola H2 habilitada, datos persistentes entre reinicios
|
||||
- `prod`: cache Thymeleaf y H2 console deshabilitada
|
||||
- `postgres`: datasource PostgreSQL, datos persistentes en el servidor o volumen Docker
|
||||
- `test`: H2 en memoria con `create-drop`, usado solo por `mvn test`
|
||||
|
||||
Con `postgres`, `run-local.sh` intenta arrancar Docker Compose automaticamente.
|
||||
|
||||
Ejemplo con PostgreSQL local:
|
||||
### PostgreSQL local
|
||||
|
||||
```bash
|
||||
SPRING_PROFILES_ACTIVE=postgres ./run-local.sh
|
||||
```
|
||||
|
||||
Ejemplo con PostgreSQL remoto:
|
||||
### PostgreSQL remoto
|
||||
|
||||
```bash
|
||||
SPRING_PROFILES_ACTIVE=postgres \
|
||||
@@ -178,44 +499,230 @@ SPRING_DATASOURCE_PASSWORD=tu_password \
|
||||
./run-local.sh
|
||||
```
|
||||
|
||||
## Variables utiles
|
||||
---
|
||||
|
||||
### Aplicacion
|
||||
## Arranque manual e IntelliJ IDEA
|
||||
|
||||
- `SERVER_PORT`
|
||||
- `SPRING_PROFILES_ACTIVE`
|
||||
- `APP_TITLE`
|
||||
Si abres el proyecto en IntelliJ (o cualquier IDE) sin usar `run-local.sh`, debes levantar Floci y configurar variables de entorno tú mismo. `run-local.sh` automatiza exactamente esos pasos.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["Abrir proyecto Maven en IntelliJ"] --> B["Java 25 como Project SDK"]
|
||||
B --> C["docker compose up floci"]
|
||||
C --> D["Esperar cognito.local.env"]
|
||||
D --> E["Configurar Run Configuration"]
|
||||
E --> F["Run BaseAdminWebApplication"]
|
||||
F --> G["http://localhost:8080/login"]
|
||||
```
|
||||
|
||||
### Requisitos previos
|
||||
|
||||
| Requisito | Versión / nota |
|
||||
|-----------|----------------|
|
||||
| JDK | **Java 25** (Temurin recomendado) |
|
||||
| Maven | Incluido en el proyecto (wrapper no obligatorio; usa el Maven del IDE) |
|
||||
| Docker | Para Cognito local (Floci) en puerto `4566` |
|
||||
| IntelliJ | Ultimate o Community (con plugin Spring Boot en Community) |
|
||||
|
||||
### 1. Importar el proyecto
|
||||
|
||||
1. **File → Open** y selecciona la carpeta raíz del repo (donde está el `pom.xml` padre).
|
||||
2. IntelliJ detecta el proyecto **Maven multi-módulo**. Espera a que termine la indexación e importación.
|
||||
3. **File → Project Structure → Project**: SDK = **Java 25**, language level = **25**.
|
||||
4. **File → Settings → Build → Build Tools → Maven → Runner**: JRE = **Project SDK (Java 25)**.
|
||||
|
||||
### 2. Levantar Floci (Cognito local)
|
||||
|
||||
En una terminal (puede ser la Terminal integrada de IntelliJ):
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.local.yml up -d floci
|
||||
```
|
||||
|
||||
La primera vez Floci ejecuta el bootstrap y escribe:
|
||||
|
||||
```
|
||||
docker/floci/generated/cognito.local.env
|
||||
```
|
||||
|
||||
Comprueba que el archivo existe (puede tardar ~30–120 s):
|
||||
|
||||
```bash
|
||||
cat docker/floci/generated/cognito.local.env
|
||||
```
|
||||
|
||||
Si no aparece, revisa los logs:
|
||||
|
||||
```bash
|
||||
docker logs base-admin-web-floci
|
||||
```
|
||||
|
||||
**PostgreSQL local:** si usas el perfil `postgres`, levanta también el servicio:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose.local.yml up -d postgres floci
|
||||
```
|
||||
|
||||
### 3. Run Configuration en IntelliJ
|
||||
|
||||
Crea una configuración **Spring Boot** (o **Application**):
|
||||
|
||||
| Campo | Valor |
|
||||
|-------|-------|
|
||||
| **Name** | `BaseAdminWebApplication` |
|
||||
| **Main class** | `mx.gob.slp.baseadminweb.jar.BaseAdminWebApplication` |
|
||||
| **Module** | `base-admin-web-jar` |
|
||||
| **Working directory** | `$PROJECT_DIR$/base-admin-web-jar` |
|
||||
| **Active profiles** | `dev` (o `postgres` si aplica) |
|
||||
|
||||
> **Working directory:** H2 guarda datos en `./data/h2/` relativo al directorio de trabajo. Debe ser `base-admin-web-jar` para que coincida con `mvn spring-boot:run` y no mezclar rutas de BD.
|
||||
|
||||
#### Variables de entorno (Cognito local)
|
||||
|
||||
En **Environment variables**, pega el contenido de `docker/floci/generated/cognito.local.env`:
|
||||
|
||||
```
|
||||
COGNITO_USER_POOL_ID=us-east-1_BaseAdminWeb
|
||||
COGNITO_REGION=us-east-1
|
||||
COGNITO_ISSUER_URI=http://localhost:4566/us-east-1_BaseAdminWeb
|
||||
COGNITO_JWK_SET_URI=http://localhost:4566/us-east-1_BaseAdminWeb/.well-known/jwks.json
|
||||
COGNITO_ADMIN_CLIENT_ID=<valor del archivo generado>
|
||||
COGNITO_SCANNER_CLIENT_ID=<valor del archivo generado>
|
||||
COGNITO_ADMIN_GROUP=admins
|
||||
COGNITO_SCANNER_GROUP=scanners
|
||||
AWS_ENDPOINT_URL=http://localhost:4566
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_ACCESS_KEY_ID=test
|
||||
AWS_SECRET_ACCESS_KEY=test
|
||||
```
|
||||
|
||||
Los `CLIENT_ID` cambian en cada bootstrap; **copia siempre los del archivo generado**, no uses valores fijos de ejemplos antiguos.
|
||||
|
||||
**Alternativa:** plugin [EnvFile](https://plugins.jetbrains.com/plugin/7861-envfile) → añade `docker/floci/generated/cognito.local.env` como archivo de entorno en la Run Configuration.
|
||||
|
||||
#### Perfil `postgres` en IntelliJ
|
||||
|
||||
Además de **Active profiles** = `postgres`, añade en Environment variables:
|
||||
|
||||
```
|
||||
SPRING_DATASOURCE_URL=jdbc:postgresql://localhost:5432/base_admin_web
|
||||
SPRING_DATASOURCE_USERNAME=postgres
|
||||
SPRING_DATASOURCE_PASSWORD=postgres
|
||||
```
|
||||
|
||||
(Y las variables Cognito del paso anterior.)
|
||||
|
||||
### 4. Ejecutar
|
||||
|
||||
1. **Build → Build Project** (o `Ctrl+F9`).
|
||||
2. Run ▶ en `BaseAdminWebApplication`.
|
||||
3. Abre http://localhost:8080/login — usuario `admin` / `Admin123*`.
|
||||
|
||||
### 5. Desarrollo sin Docker (modo local puro)
|
||||
|
||||
Si no quieres Floci, desactiva Cognito y usa usuarios de la BD:
|
||||
|
||||
| Variable | Valor |
|
||||
|----------|-------|
|
||||
| `APP_COGNITO_ENABLED` | `false` |
|
||||
|
||||
No hace falta `AWS_ENDPOINT_URL` ni levantar Docker. Los usuarios `admin` y `scanner` se crean al arrancar vía `InitialDataLoader`.
|
||||
|
||||
El login API devuelve JWT local (HS256); **refresh no está disponible** en este modo.
|
||||
|
||||
### 6. Checklist de problemas frecuentes
|
||||
|
||||
| Síntoma | Causa probable | Solución |
|
||||
|---------|----------------|----------|
|
||||
| Login web falla con Cognito habilitado | Floci no está arriba o faltan env vars | `docker compose up -d floci` + cargar `cognito.local.env` |
|
||||
| Login va a AWS real en local | Falta `AWS_ENDPOINT_URL` | Definir `AWS_ENDPOINT_URL=http://localhost:4566` |
|
||||
| `Invalid client id` | Client IDs desactualizados | Regenerar con bootstrap y copiar de `cognito.local.env` |
|
||||
| H2 vacío o ruta rara | Working directory incorrecto | `$PROJECT_DIR$/base-admin-web-jar` |
|
||||
| Error de versión Java | SDK distinto a 25 | Project Structure → SDK Java 25 |
|
||||
| Tests en IDE fallan por Cognito | Perfil `test` desactiva Cognito | Ejecutar tests con perfil `test` (ya configurado en `application-test.yml`) |
|
||||
|
||||
### 7. Comandos útiles desde IntelliJ
|
||||
|
||||
| Acción | Comando Maven (ventana Maven o terminal) |
|
||||
|--------|------------------------------------------|
|
||||
| Compilar todo | `mvn -pl base-admin-web-jar -am -DskipTests install` |
|
||||
| Tests | `mvn test` |
|
||||
| Solo módulo jar | clic derecho en `base-admin-web-jar` → Run Maven → `spring-boot:run` (tras exportar env vars en la shell) |
|
||||
|
||||
---
|
||||
|
||||
## Perfiles Spring
|
||||
|
||||
| Perfil | Base de datos | Cognito | Uso |
|
||||
|--------|---------------|---------|-----|
|
||||
| `dev` (default) | H2 en archivo `./data/h2/` | Floci (manual o `run-local.sh`) | Desarrollo diario |
|
||||
| `postgres` | PostgreSQL | Floci | Desarrollo con BD real |
|
||||
| `prod` | Configurable | AWS | Producción |
|
||||
| `test` | H2 en memoria `create-drop` | Deshabilitado | `mvn test` |
|
||||
|
||||
---
|
||||
|
||||
## Variables de entorno
|
||||
|
||||
### Aplicación
|
||||
|
||||
| Variable | Default | Descripción |
|
||||
|----------|---------|-------------|
|
||||
| `SERVER_PORT` | `8080` | Puerto HTTP |
|
||||
| `SPRING_PROFILES_ACTIVE` | `dev` | Perfil Spring |
|
||||
| `APP_TITLE` | `Base Admin Web` | Título en vistas |
|
||||
|
||||
### Cognito
|
||||
|
||||
| Variable | Local (Floci) | Producción (AWS) |
|
||||
|----------|---------------|------------------|
|
||||
| `APP_COGNITO_ENABLED` | `true` | `true` |
|
||||
| `COGNITO_REGION` | `us-east-1` | tu región |
|
||||
| `COGNITO_USER_POOL_ID` | `us-east-1_BaseAdminWeb` | `us-east-1_XXXXX` |
|
||||
| `COGNITO_ISSUER_URI` | `http://localhost:4566/...` | `https://cognito-idp.{region}.amazonaws.com/{poolId}` |
|
||||
| `COGNITO_JWK_SET_URI` | `http://localhost:4566/.../jwks.json` | `https://cognito-idp.{region}.amazonaws.com/{poolId}/.well-known/jwks.json` |
|
||||
| `COGNITO_ADMIN_CLIENT_ID` | generado por Floci | client del pool |
|
||||
| `COGNITO_SCANNER_CLIENT_ID` | generado por Floci | client del pool |
|
||||
| `COGNITO_ADMIN_GROUP` | `admins` | `admins` |
|
||||
| `COGNITO_SCANNER_GROUP` | `scanners` | `scanners` |
|
||||
| `AWS_ENDPOINT_URL` | `http://localhost:4566` | **no definir** |
|
||||
|
||||
### Base de datos
|
||||
|
||||
- `SPRING_DATASOURCE_URL`
|
||||
- `SPRING_DATASOURCE_USERNAME`
|
||||
- `SPRING_DATASOURCE_PASSWORD`
|
||||
- `SPRING_JPA_HIBERNATE_DDL_AUTO`
|
||||
| Variable | Descripción |
|
||||
|----------|-------------|
|
||||
| `SPRING_DATASOURCE_URL` | JDBC URL |
|
||||
| `SPRING_DATASOURCE_USERNAME` | Usuario BD |
|
||||
| `SPRING_DATASOURCE_PASSWORD` | Contraseña BD |
|
||||
| `SPRING_JPA_HIBERNATE_DDL_AUTO` | `update` por defecto |
|
||||
|
||||
### JWT API
|
||||
### JWT local (solo `APP_COGNITO_ENABLED=false`)
|
||||
|
||||
- `APP_JWT_SECRET` (minimo 32 caracteres)
|
||||
- `APP_JWT_EXPIRATION_HOURS`
|
||||
| Variable | Descripción |
|
||||
|----------|-------------|
|
||||
| `APP_JWT_SECRET` | Secreto HS256 (mín. 32 caracteres) |
|
||||
| `APP_JWT_EXPIRATION_HOURS` | Expiración del token |
|
||||
|
||||
## Seguridad
|
||||
### Usuarios semilla (modo local sin Cognito)
|
||||
|
||||
- Panel web: autenticacion por formulario con usuarios en base de datos
|
||||
- API de escaneo: autenticacion stateless con JWT
|
||||
- `/admin/**` y `/dashboard`: requieren rol `ADMIN`
|
||||
- `/api/pass-codes/scan`: requiere rol `SCANNER` y token Bearer
|
||||
- `/api/auth/login`: publico, pero solo emite token a usuarios `SCANNER`
|
||||
| Variable | Default |
|
||||
|----------|---------|
|
||||
| `APP_ADMIN_USERNAME` / `APP_ADMIN_PASSWORD` | `admin` / `Admin123*` |
|
||||
| `APP_SCANNER_USERNAME` / `APP_SCANNER_PASSWORD` | `scanner` / `Scanner123*` |
|
||||
|
||||
---
|
||||
|
||||
## Objetivo de esta base
|
||||
|
||||
Esta base deja resuelto lo minimo para arrancar nuevos proyectos internos:
|
||||
Esta base deja resuelto lo mínimo para arrancar nuevos proyectos internos:
|
||||
|
||||
- autenticacion local funcional para administradores
|
||||
- autenticacion API para escaneo movil o externo
|
||||
- layout inicial con Metronic
|
||||
- dashboard simple
|
||||
- modulo de codigos pase con QR y API de escaneo
|
||||
- estructura modular limpia
|
||||
- soporte rapido para H2 y PostgreSQL
|
||||
- Autenticación Cognito (local y AWS) con fallback a BD local
|
||||
- Panel web con Metronic y sesión HTTP para administradores
|
||||
- API stateless con JWT para escaneo móvil o externo
|
||||
- App Flutter de referencia para escaneo QR
|
||||
- Módulo de códigos pase con alta, listado, QR y API de escaneo
|
||||
- Estructura modular Maven limpia
|
||||
- Soporte rápido para H2 y PostgreSQL
|
||||
- Floci para desarrollo sin depender de AWS
|
||||
|
||||
El dashboard actual se mantiene deliberadamente sencillo para servir como punto de partida para futuras integraciones.
|
||||
El dashboard se mantiene deliberadamente sencillo como punto de partida para futuras integraciones.
|
||||
Reference in New Issue
Block a user