Otro post más, se verá algo de bases de datos, springboot, java, xml, h2, flyway para la migración de h2 a postgreSQL, securizar un poco en postgreSQL y listo, más de lo mismo.
Migrando Script de h2 a PostgreSQL
Migracion
|
Propiedades para el h2
### spring config
spring:
datasource:
url: jdbc:h2:mem:db (1)
username: test
password: test
1 | db: es el nombre de nuestra bases de datos. |
Depedencias para nuestro pom.xml
Ya estoy esta con un parent de spring y por lo tanto la version la hereda, por eso no esta el tag <version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.6</version>
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
En esta versión de springboot 3.3.6 debe estar habilitado en nuestra seguridad la consola de h2 , para poder acceder a ella
al menos en desarrollo.
|
@Bean
public WebSecurityCustomizer ignoringCustomizer() {
return (web) -> web.ignoring().requestMatchers("/h2-console/**");
}
Desde la UI cuando nos conectamos podemos hacer esto
Tips
|
SCRIPT TO 'dump.sql' (1)
1 | Esto creará un fichero llamado dump.sql dentro el directorio del proyecto, no hace falta copiar y pegar 😚 |
Renombrado
|
El Docker compose
Necesitamos tener postgresql levantado al menos en local, o usar testcontainer.
services:
postgres:
image: 'postgres:latest'
environment:
- 'POSTGRES_DB=mydatabase'
- 'POSTGRES_PASSWORD=secret'
- 'POSTGRES_USER=myuser'
ports:
- '5432:5432'
Reconfigurando properties de Springboot
Flyway debe encargarse de la estructura de la base de datos, por lo que es importante desactivar la auto-creación de Hibernate.
### spring config
spring:
jpa:
defer-datasource-initialization: true
show-sql: true
generate-ddl: false (1)
hibernate
ddl-auto: none (2)
properties:
hibernate:
format_sql: true
#Config Flyway
flyway:
enabled: true
baseline-on-migrate: true
out-of-order: true
datasource:
url: jdbc:postgresql://localhost:5432/mydatabase
username: myuser
password: secret
driverClassName: org.postgresql.Driver (3)
1 | desactivado. |
2 | desactivado. |
3 | Esta propiedad se supone que ya es opcional. |
Esto evita que Hibernate intente modificar la base de datos antes de que Flyway haga su trabajo.
Actualizamos el pom.xml
Con estas dependencias del driver de postgresql y flyway
<!--<dependency>
<groupId>com.h2database</groupId> (1)
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency> -->
<!-- https://mvnrepository.com/artifact/org.flywaydb/flyway-core -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>11.3.0</version> (2)
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-database-postgresql</artifactId> (3)
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
1 | Dependencia de h2 comentada. |
2 | Importante tenerla para evitar errores tipo Caused by: org.flywaydb.core.api.FlywayException: Unsupported Database: PostgreSQL 17.2 |
3 | Importante también. |
Posible error en el arranque, con jpa y flyway
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Failed to initialize dependency 'flyway' of LoadTimeWeaverAware bean 'entityManagerFactory': Error creating bean with name 'flyway' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Circular depends-on relationship between 'flyway' and 'entityManagerFactory'
Lo más probable es tengamos pospuesta la inicialización del Datasource en nuestro fichero de propiedades, debemos tenerlo así, con false:
-
defer-datasource-initialization: false
Caso con db y datos existentes
La configuración cuando tenemos datos existentes en la db, y no queremos tocar nada de nuestros datos, debemos cambiar las propiedades por ejemplo:
flyway:
schema: nuestro-schema
baseline-on-migrate: true
baseline-version: 0
clean-disabled: true
validate-on-migrate: false
Además crear un fichero, en db.migration
por ejemplo: V1.1__baseline.sql
con esto:
-- Baseline migration - estructura existente
Cuando arranquemos flyway el va a versionar este script, en el schema publico de nuestra db.
SELECT * FROM flyway_schema_history;
installed_rank|version|description|type|script |checksum |installed_by|installed_on |execution_time|success|
--------------+-------+-----------+----+------------------+----------+------------+-----------------------+--------------+-------+
1|1.1 |baseline |SQL |V1.1__baseline.sql|-385248959|admin |fecha | 6|true |
Para un futuro podemos crear nuevas migraciones siguiendo la convención de nombres de Flyway:
V1.2__descripcion.sql
V1.3__descripcion.sql
Pendiente con esto:
|
Algunas configuraciones para el IDE
Si actualizamos el fichero .bashrc del usuario con variables de entorno, debemos reiniciar el IDE para que las variables se refresquen correctamente, sino apuntaran a versiones incorrectas.
|
El profile de spring
Si tenemos varios profile debemos estar atentos a cargar el profile correcto, local y/o algun otro.
-
Para local desde el IDE, podemos usar
--spring.profiles.active=local
aquí:
Por lo tanto en la consola debemos ver lo siguient:
2025-02-10T20:30:14.347+01:00 INFO 1228306 --- [ restartedMain] c.x.x.Application : The following 1 profile is active: "local" (1)
1 | El profile de local, application-local.yml cargado correctamente. |
Cargando correctamente el .bashrc del IDE
Es importante para asegurar nos que nuestro ide carge el .bashrc correctamente donde tenemos las variables de entorno establecidas como en la imagen, es bastante util.
/bin/bash --login
/home/rubn/.local/share/applications/IntelliJ.desktop
#!/usr/bin/env xdg-open
[Desktop Entry]
Type=Application
Name=IntelliJ
StartupWMClass=jetbrains-idea-ce
Exec=/bin/bash -i -c "/home/rubn/Downloads/jdk-ide/idea/bin/idea" %f (1)
Icon=/home/rubn/Downloads/jdk-ide/idea/bin/idea.svg
Categories=Network;Application;
1 | Aquí forzamos la carga de modo interactivo con el parametro -i |
Cada cambio en dichero fichero le toca un reinicio con source .bashrc
o el fichero donde tengamos dichas propiedades.
Instalando PostgreSQL FreeBSD
La wiki en nuestra amiga PostgreSQL/Setup
Un poco de postgres con springboot para securizar |
La seguridad suele ser importante siempre se usar para la prueba la version de postgres 15, para ellos habilitamos rapidamente un jail con bastille usando la template por ejemplo esta
aplicandolo al jail existente llamado postgresql
bastille template TARGET yaazkal/bastille-postgres (1)
1 | TARGET es el nombre de nuestro jail |
Solo con acceso desde localhost
Si, tengo solo habilitado usar el psql
desde el propio server con esta linea en el fichero pg_hba.conf
# "local" is for Unix domain socket connections only
local all all trust (1)
1 | las otra lineas por recomendación mejor que esten con scram-sha-256 por trust o MD5 |
Migrando password a SCRAM
Revisar esto |
El fichero postgresql.conf
toca actualizar la línea
# - Authentication -
#authentication_timeout = 1min # 1s-600s
password_encryption = scram-sha-256 # scram-sha-256 or md5 (1)
1 | Aqui descomentar y usar scram-sha-256 |
postgres=#
SELECT rolname, rolpassword ~ '^SCRAM-SHA-256\$' AS has_upgraded
FROM pg_authid
WHERE rolcanlogin;
rolname | has_upgraded
----------+--------------
postgres |
admin | t (1)
(2 rows)
1 | Indica true, que ya esta actualizada |
Posiblemente para autenticarnos desde nuestro cliente java con spring tengas ciertos problemas como
Caused by: org.postgresql.util.PSQLException: FATAL: password authentication failed for user "admin"
Actualización recomentada
$ psql
psql (14.15)
Type "help" for help.
postgres=# \password admin (1)
Enter new password for user "admin":
1 | Usando \password se hace la actualizacion correcta de las password 🔥 |
ALTER ROLE admin WITH PASSWORD 'tu_contraseña'; (1)
1 | Esto no es recomendado para la actualización, ademas de dejar rastro en el histórico. |
Para reiniciar db
service postgresql reload
El comando \du
Nos permite ver los roles con sus atributos
postgres=# \du
List of roles
Role name | Attributes | Member of
-----------+------------------------------------------------------------+-----------
admin | Create DB | {}
postgres | Superuser, Create role, Create DB, Replication, Bypass RLS | {}
Importando Schemas
Para hacerlo podemos usar el comando
psql -U \ (1)
admin -d \ (2)
ott -a -f /var/db/postgres/ott-schema.sql (3)
1 | PostgreSQL en modo interactivo, con el parametro de -U de usuario. |
2 | El comando -d para la database ott. |
3 | El comando -a imprime todas la lineas, y -f es la ruta donde el usuario postgres tiene permisos de lectura, necesario para import dicho .sql . |
Encoding para la password
Si usamos por defecto el bean JdbcUserDetailsManager
por el InMemoryUserDetailsManager
@Bean
public JdbcUserDetailsManager jdbcUserDetailsManager(DataSource dataSource) {
return new JdbcUserDetailsManager(dataSource);
}
Debemos añadir un encoder por defecto al menos
java.lang.IllegalArgumentException: Given that there is no default password encoder configured, each password must have a password encoding prefix. Please either prefix this password with '{noop}' or set a default password encoder in `DelegatingPasswordEncoder`.
En el hash por defecto de spring security es un SHA-256
username|password |enabled|
--------+--------------------------------------------------------------------------------+-------+
boby |843b103f6c09de407d366f8ff6553691767a35dbaf870cad47e86945c4103be85ee8ea49509b9502|true |
chispa |a94f80ed1a6677dedb489a6912e6c83a7c2a7ada2c4d739dd4a4ab9e454d3d875134fa4480b19c55|true |
Actualizamos añadiendo un template del hash
UPDATE users SET password = '{sha256}' || password;
username|password |enabled|
--------+----------------------------------------------------------------------------------------+-------+
boby |{sha256}843b103f6c09de407d366f8ff6553691767a35dbaf870cad47e86945c4103be85ee8ea49509b9502|true |
chispa |{sha256}a94f80ed1a6677dedb489a6912e6c83a7c2a7ada2c4d739dd4a4ab9e454d3d875134fa4480b19c55|true |
Si aplicamos este otro bean y nos logueamos en el login de spring security el password encoder se cambiara a bcrypt
bajo fondo.
@Bean
public UserDetailsPasswordService userDetailsPasswordService(UserDetailsManager userDetailsManager) {
return (user, newPassword) -> {
var updated = User.withUserDetails(user)
.password(newPassword)
.build();
userDetailsManager.updateUser(updated);
return updated;
};
}
Migración bajo fondo a bcrypt
username|password |enabled|
--------+----------------------------------------------------------------------------------------+-------+
boby |{sha256}843b103f6c09de407d366f8ff6553691767a35dbaf870cad47e86945c4103be85ee8ea49509b9502|true |
chispa |{bcrypt}$2a$10$dMqSFw875ZLvpsADb24XDezNMacIYqC4bIUkbb24QOLYE3pmlZInS |true |
Ya ese bcrypt nos dice que fue un update existoso
Secretos desde el vault con TLS
Como ya tenemos configurado nuestro vault con SSL/TLS
podemos traernos los secretos usando certificado para autenticarnos.
De momento al intentar hacerlo desde spring tenemos lo siguiente, en nuestro fichero de propiedad
### spring config
spring:
### Vault
config:
import: vault://
cloud:
vault:
uri: ${URL_VAULT} (1)
token: ${INITIAL_ROOT_TOKEN} (2)
kv:
enabled: true
backend: app
application-name: nombre-app
ssl:
key-store: classpath:/cert/vault-cert.p12 # O file:/ruta/completa/mi-certificado.p12
key-store-password: pass # La passphrase de nuestro certificado
key-store-type: PKCS12
1 | Url del vault, en https |
2 | El initial root token |
org.springframework.vault.VaultException: Status 400 Bad Request [path/secreto]: Client sent an HTTP request to an HTTPS server. (1)
1 | Intento de conexion de secretos via http normal, sin certificado, el server nos rechaza a la hora de traernos los secretos. |
Como ya tenemos nuestro certificado de cliente .p12 debemos usarlo para autenticarnos por ejemplo con keytool para importar el ca:
keytool -import -trustcacerts -alias vault-ca -file ca-cert.crt -keystore vault-truststore.jks
...
...
Trust this certificate? [no]: yes (1)
Certificate was added to keystore
1 | Escribimos yes |
Deberíamos tener generado el vault-truststore.jks
entonces, ahora para probar la conexión
Ahora añadimos propiedades trust-store
al src/main/resources/cert
para prueba rápida.
FYI Sobre file
|
ssl:
(1)
key-store: file:src/main/resources/cert/vault-cert.p12 # O file:/ruta/completa/mi-certificado.p12
key-store-password: pass # La passphrase de nuestro certificado
key-store-type: PKCS12
# Trust store para validar el certificado del servidor
trust-store: file:src/main/resources/cert/vault-truststore.jks
trust-store-password: pass (2)
trust-store-type: JKS
1 | Por la brevedad/seguridad usaremos file, también esta classpath cuando usaremos el .jar final de PROD. |
2 | La password introducida al ejecutar el comando keytool. |
La config de spring para mapping de los secretos
Ya los secretos los tenemos disponibles en nuestro vault, ahora los podemos mappear a nuestro objeto Java, via @ConfigurationProperties
Explicitamente en el vault, tengo un secret seguido de un punto . para usarlo con el bean de spring y hacer mapping más facil. |
@Data
@Configuration
@ConfigurationProperties("secret") (1)
public class SecretsConfiguration {
private String chispita;
}
1 | Indicando que, en el vault estaría secret.chispita con el valor del secreto. |