diff --git a/010_spring_boot/api_rest/api2/pom.xml b/010_spring_boot/api_rest/api2/pom.xml
index dcad58c..058c2f6 100644
--- a/010_spring_boot/api_rest/api2/pom.xml
+++ b/010_spring_boot/api_rest/api2/pom.xml
@@ -21,7 +21,6 @@
org.springframework.boot
spring-boot-starter-web
-
org.springframework.boot
spring-boot-devtools
@@ -63,6 +62,15 @@
spring-boot-starter-validation
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
diff --git a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/controller/AutenticacionController.java b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/controller/AutenticacionController.java
new file mode 100644
index 0000000..d29d8af
--- /dev/null
+++ b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/controller/AutenticacionController.java
@@ -0,0 +1,35 @@
+package med.voll.api.controller;
+
+import jakarta.validation.Valid;
+import med.voll.api.domain.usuario.DatosAutenticacionUsuario;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/login")
+public class AutenticacionController {
+
+ @Autowired
+ private AuthenticationManager authenticationManager;
+
+ @PostMapping
+ public ResponseEntity autenticarUsuario(@RequestBody @Valid DatosAutenticacionUsuario datosAutenticacionUsuario) {
+ Authentication token = new UsernamePasswordAuthenticationToken(
+ datosAutenticacionUsuario.login(),
+ datosAutenticacionUsuario.clave());
+ authenticationManager.authenticate(token);
+ //var token = new UsernamePasswordAuthenticationToken(
+ // datosAutenticacionUsuario.login(),
+ // datosAutenticacionUsuario.clave());
+ //var autentication = authenticationManager.authenticate(token);
+ return ResponseEntity.ok().build();
+
+ }
+}
diff --git a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/controller/MedicoController.java b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/controller/MedicoController.java
index 0089637..bb21553 100644
--- a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/controller/MedicoController.java
+++ b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/controller/MedicoController.java
@@ -29,21 +29,23 @@ public class MedicoController {
UriComponentsBuilder uriComponentsBuilder) {
Medico medico = medicoRepository.save(new Medico(datosRegistroMedico));
DatosRespuestaMedico datosRespuestaMedico = new DatosRespuestaMedico(
- medico.getId(), medico.getNombre(), medico.getEmail(), medico.getTelefono(), medico.getDocumento(),
- new DatosDireccion(
- medico.getDireccion().getCalle(), medico.getDireccion().getDistrito(), medico.getDireccion().getCiudad(),
- medico.getDireccion().getNumero(), medico.getDireccion().getComplemento()
- )
+ medico, new DatosDireccion(medico.getDireccion())
);
+
+ //DatosRespuestaMedico datosRespuestaMedico = new DatosRespuestaMedico(
+ // medico.getId(), medico.getNombre(), medico.getEmail(), medico.getTelefono(), medico.getDocumento(),
+ // new DatosDireccion(
+ // medico.getDireccion().getCalle(), medico.getDireccion().getDistrito(), medico.getDireccion().getCiudad(),
+ // medico.getDireccion().getNumero(), medico.getDireccion().getComplemento()
+ // )
+ //);
URI url = uriComponentsBuilder.path("/medicos/{id}") .buildAndExpand(medico.getId()).toUri();
return ResponseEntity.created(url).body(datosRespuestaMedico);
- // Debe retornar 201 Created
- // Url donde encontrar al médico ej. http://127.0.0.1:8080/medicos/xx
}
@GetMapping
- public ResponseEntity> listadoMedicos(@PageableDefault(size = 5) Pageable paginacion) {
- //return medicoRepository.findAll(paginacion).map(DatosListadoMedicos::new);
+ public ResponseEntity> listadoMedicos(
+ @PageableDefault(size = 5) Pageable paginacion) {
return ResponseEntity.ok(medicoRepository.findByActivoTrue(paginacion).map(DatosListadoMedicos::new));
}
@@ -53,16 +55,12 @@ public class MedicoController {
@RequestBody @Valid DatosActualizarMedico datosActualizarMedico) {
Medico medico = medicoRepository.getReferenceById(datosActualizarMedico.id());
medico.actualizarDatos(datosActualizarMedico);
- return ResponseEntity.ok(new DatosRespuestaMedico(
- medico.getId(), medico.getNombre(), medico.getEmail(), medico.getTelefono(), medico.getDocumento(),
- new DatosDireccion(
- medico.getDireccion().getCalle(), medico.getDireccion().getDistrito(), medico.getDireccion().getCiudad(),
- medico.getDireccion().getNumero(), medico.getDireccion().getComplemento()
- )
- ));
+ DatosRespuestaMedico datosRespuestaMedico = new DatosRespuestaMedico(
+ medico, new DatosDireccion(medico.getDireccion())
+ );
+ return ResponseEntity.ok(datosRespuestaMedico);
}
- // Desactivar Medico
@DeleteMapping("/{id}")
@Transactional
public ResponseEntity eliminarMedico(@PathVariable Long id) {
@@ -74,14 +72,10 @@ public class MedicoController {
@GetMapping("/{id}")
public ResponseEntity retornaDatosMedico(@PathVariable Long id) {
Medico medico = medicoRepository.getReferenceById(id);
- var datosMedico = new DatosRespuestaMedico(
- medico.getId(), medico.getNombre(), medico.getEmail(), medico.getTelefono(), medico.getDocumento(),
- new DatosDireccion(
- medico.getDireccion().getCalle(), medico.getDireccion().getDistrito(), medico.getDireccion().getCiudad(),
- medico.getDireccion().getNumero(), medico.getDireccion().getComplemento()
- )
+ DatosRespuestaMedico datosRespuestaMedico = new DatosRespuestaMedico(
+ medico, new DatosDireccion(medico.getDireccion())
);
- return ResponseEntity.ok(datosMedico);
+ return ResponseEntity.ok(datosRespuestaMedico);
}
}
diff --git a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/controller/PacienteController.java b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/controller/PacienteController.java
index 0c1fea5..3b978ee 100644
--- a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/controller/PacienteController.java
+++ b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/controller/PacienteController.java
@@ -29,35 +29,30 @@ public class PacienteController {
UriComponentsBuilder uriComponentsBuilder) {
Paciente paciente = pacienteRepository.save(new Paciente(datosRegistroPaciente));
DatosRespuestaPaciente datosRespuestaPaciente = new DatosRespuestaPaciente(
- paciente.getId(), paciente.getNombre(), paciente.getEmail(), paciente.getTelefono(), paciente.getDocumento(),
- new DatosDireccion(
- paciente.getDireccion().getCalle(), paciente.getDireccion().getDistrito(), paciente.getDireccion().getCiudad(),
- paciente.getDireccion().getNumero(), paciente.getDireccion().getComplemento()
- )
+ paciente, new DatosDireccion(paciente.getDireccion())
);
URI url = uriComponentsBuilder.path("/pacientes/{id}") .buildAndExpand(paciente.getId()).toUri();
return ResponseEntity.created(url).body(datosRespuestaPaciente);
}
@GetMapping
- public ResponseEntity> listadoPacientes(@PageableDefault(size = 5) Pageable paginacion) {
- //return pacienteRepository.findAll(paginacion).map(DatosListadoPacientes::new);
- return ResponseEntity.ok(pacienteRepository.findByActivoTrue(paginacion).map(DatosListadoPacientes::new));
+ public ResponseEntity> listadoPacientes(
+ @PageableDefault(size = 5) Pageable paginacion) {
+ return ResponseEntity.ok(
+ pacienteRepository.findByActivoTrue(paginacion).map(DatosListadoPacientes::new)
+ );
}
@PutMapping
@Transactional
- public ResponseEntity actualizarPaciente(@RequestBody @Valid DatosActualizarPaciente datosActualizarPaciente) {
+ public ResponseEntity actualizarPaciente(
+ @RequestBody @Valid DatosActualizarPaciente datosActualizarPaciente) {
Paciente paciente = pacienteRepository.getReferenceById(datosActualizarPaciente.id());
paciente.actualizarDatos(datosActualizarPaciente);
- return ResponseEntity.ok(new DatosRespuestaPaciente(
- paciente.getId(), paciente.getNombre(), paciente.getEmail(), paciente.getTelefono(), paciente.getDocumento(),
- new DatosDireccion(
- paciente.getDireccion().getCalle(), paciente.getDireccion().getDistrito(),
- paciente.getDireccion().getCiudad(),paciente.getDireccion().getNumero(),
- paciente.getDireccion().getComplemento()
- )
- ));
+ DatosRespuestaPaciente datosRespuestaPaciente = new DatosRespuestaPaciente(
+ paciente, new DatosDireccion(paciente.getDireccion())
+ );
+ return ResponseEntity.ok(datosRespuestaPaciente);
}
// Desactivar Paciente
@@ -72,14 +67,10 @@ public class PacienteController {
@GetMapping("/{id}")
public ResponseEntity retornaDatosPaciente(@PathVariable Long id) {
Paciente paciente = pacienteRepository.getReferenceById(id);
- var datosPaciente = new DatosRespuestaPaciente(
- paciente.getId(), paciente.getNombre(), paciente.getEmail(), paciente.getTelefono(), paciente.getDocumento(),
- new DatosDireccion(
- paciente.getDireccion().getCalle(), paciente.getDireccion().getDistrito(), paciente.getDireccion().getCiudad(),
- paciente.getDireccion().getNumero(), paciente.getDireccion().getComplemento()
- )
+ DatosRespuestaPaciente datosRespuestaPaciente = new DatosRespuestaPaciente(
+ paciente, new DatosDireccion(paciente.getDireccion())
);
- return ResponseEntity.ok(datosPaciente);
+ return ResponseEntity.ok(datosRespuestaPaciente);
}
}
diff --git a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/direccion/DatosDireccion.java b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/direccion/DatosDireccion.java
index 1cb9e60..5e35446 100644
--- a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/direccion/DatosDireccion.java
+++ b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/direccion/DatosDireccion.java
@@ -8,4 +8,13 @@ public record DatosDireccion(
@NotBlank String ciudad,
String numero,
String complemento) {
+
+ public DatosDireccion(Direccion direccion){
+ this(direccion.getCalle(),
+ direccion.getDistrito(),
+ direccion.getCiudad(),
+ direccion.getNumero(),
+ direccion.getComplemento()
+ );
+ }
}
diff --git a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/medico/DatosRespuestaMedico.java b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/medico/DatosRespuestaMedico.java
index f1daa18..cdf1753 100644
--- a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/medico/DatosRespuestaMedico.java
+++ b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/medico/DatosRespuestaMedico.java
@@ -6,4 +6,14 @@ import med.voll.api.domain.direccion.DatosDireccion;
public record DatosRespuestaMedico(@NotNull Long id, String nombre,
String email, String telefono, String documento,
DatosDireccion direccion) {
+
+ public DatosRespuestaMedico(Medico medico, DatosDireccion direccion){
+ this(medico.getId(),
+ medico.getNombre(),
+ medico.getEmail(),
+ medico.getTelefono(),
+ medico.getDocumento(),
+ direccion);
+ }
+
}
diff --git a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/paciente/DatosRespuestaPaciente.java b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/paciente/DatosRespuestaPaciente.java
index aba441f..9cfba30 100644
--- a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/paciente/DatosRespuestaPaciente.java
+++ b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/paciente/DatosRespuestaPaciente.java
@@ -6,4 +6,14 @@ import med.voll.api.domain.direccion.DatosDireccion;
public record DatosRespuestaPaciente(@NotNull Long id, String nombre,
String email, String telefono, String documento,
DatosDireccion direccion) {
+
+ public DatosRespuestaPaciente(Paciente paciente, DatosDireccion direccion){
+ this(paciente.getId(),
+ paciente.getNombre(),
+ paciente.getEmail(),
+ paciente.getTelefono(),
+ paciente.getDocumento(),
+ direccion);
+ }
+
}
diff --git a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/usuario/DatosAutenticacionUsuario.java b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/usuario/DatosAutenticacionUsuario.java
new file mode 100644
index 0000000..6881ca6
--- /dev/null
+++ b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/usuario/DatosAutenticacionUsuario.java
@@ -0,0 +1,5 @@
+package med.voll.api.domain.usuario;
+
+public record DatosAutenticacionUsuario(String login, String clave) {
+
+}
diff --git a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/usuario/Usuario.java b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/usuario/Usuario.java
new file mode 100644
index 0000000..d005768
--- /dev/null
+++ b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/usuario/Usuario.java
@@ -0,0 +1,63 @@
+package med.voll.api.domain.usuario;
+
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+import java.util.Collection;
+import java.util.List;
+
+@Table(name = "usuarios")
+@Entity(name = "Usuario")
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(of = "id")
+public class Usuario implements UserDetails {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+ private String login;
+ private String clave;
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return List.of(new SimpleGrantedAuthority("ROLE_USER"));
+ }
+
+ @Override
+ public String getPassword() {
+ return clave;
+ }
+
+ @Override
+ public String getUsername() {
+ return login;
+ }
+
+ @Override
+ public boolean isAccountNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isAccountNonLocked() {
+ return true;
+ }
+
+ @Override
+ public boolean isCredentialsNonExpired() {
+ return true;
+ }
+
+ @Override
+ public boolean isEnabled() {
+ return true;
+ }
+}
diff --git a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/usuario/UsuarioRepository.java b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/usuario/UsuarioRepository.java
new file mode 100644
index 0000000..f7c70d8
--- /dev/null
+++ b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/domain/usuario/UsuarioRepository.java
@@ -0,0 +1,9 @@
+package med.voll.api.domain.usuario;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+
+public interface UsuarioRepository extends JpaRepository {
+ UserDetails findByLogin(String login);
+}
diff --git a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/infra/ManejadorDeErrores.java b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/infra/errores/ManejadorDeErrores.java
similarity index 74%
rename from 010_spring_boot/api_rest/api2/src/main/java/med/voll/api/infra/ManejadorDeErrores.java
rename to 010_spring_boot/api_rest/api2/src/main/java/med/voll/api/infra/errores/ManejadorDeErrores.java
index c52edef..fe6db0c 100644
--- a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/infra/ManejadorDeErrores.java
+++ b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/infra/errores/ManejadorDeErrores.java
@@ -1,6 +1,7 @@
-package med.voll.api.infra;
+package med.voll.api.infra.errores;
import jakarta.persistence.EntityNotFoundException;
+import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
@@ -21,6 +22,12 @@ public class ManejadorDeErrores {
return ResponseEntity.badRequest().body(errores);
}
+ @ExceptionHandler(DataIntegrityViolationException.class)
+ public ResponseEntity manejarError500(DataIntegrityViolationException e) {
+ var errores = e.getMostSpecificCause().getLocalizedMessage();
+ return ResponseEntity.badRequest().body(errores);
+ }
+
private record DatosErrorValidacion(String campo, String error) {
public DatosErrorValidacion(FieldError error) {
this(error.getField(), error.getDefaultMessage());
diff --git a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/infra/security/AutenticacionService.java b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/infra/security/AutenticacionService.java
new file mode 100644
index 0000000..14f0add
--- /dev/null
+++ b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/infra/security/AutenticacionService.java
@@ -0,0 +1,20 @@
+package med.voll.api.infra.security;
+
+import med.voll.api.domain.usuario.UsuarioRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.security.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Service;
+
+@Service
+public class AutenticacionService implements UserDetailsService {
+
+ @Autowired
+ private UsuarioRepository usuarioRepository;
+
+ @Override
+ public UserDetails loadUserByUsername(String login) throws UsernameNotFoundException {
+ return usuarioRepository.findByLogin(login);
+ }
+}
diff --git a/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/infra/security/SecurityConfigurations.java b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/infra/security/SecurityConfigurations.java
new file mode 100644
index 0000000..a5b1137
--- /dev/null
+++ b/010_spring_boot/api_rest/api2/src/main/java/med/voll/api/infra/security/SecurityConfigurations.java
@@ -0,0 +1,35 @@
+package med.voll.api.infra.security;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.web.SecurityFilterChain;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfigurations {
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
+ return httpSecurity.csrf().disable().sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().build();
+ }
+
+ @Bean
+ public AuthenticationManager authenticationManager(
+ AuthenticationConfiguration authenticationConfiguration) throws Exception {
+ return authenticationConfiguration.getAuthenticationManager();
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder () {
+ return new BCryptPasswordEncoder();
+ }
+
+}
diff --git a/010_spring_boot/api_rest/api2/src/main/resources/db/migration/V5__create-table-usuarios.sql b/010_spring_boot/api_rest/api2/src/main/resources/db/migration/V5__create-table-usuarios.sql
new file mode 100644
index 0000000..bf6e084
--- /dev/null
+++ b/010_spring_boot/api_rest/api2/src/main/resources/db/migration/V5__create-table-usuarios.sql
@@ -0,0 +1,9 @@
+create table usuarios(
+
+ id bigint not null auto_increment,
+ login varchar(100) not null unique,
+ clave varchar(300) not null,
+
+ primary key(id)
+
+);
\ No newline at end of file
diff --git a/010_spring_boot/spring_boot_2.md b/010_spring_boot/spring_boot_2.md
index 289a3a4..a2f4a51 100644
--- a/010_spring_boot/spring_boot_2.md
+++ b/010_spring_boot/spring_boot_2.md
@@ -304,3 +304,263 @@ public record DatosRegistroMedico(
@Valid DatosDireccion direccion) {}
```
+### Seguridad
+
+- Autenticación
+- Autorización
+- Protección contra ataques (CSRF, clickjacking)
+
+```mermaid
+%%{init: {'theme': 'dark','themeVariables': {'clusterBkg': '#2b2f38'}, 'flowchart': {'curve': 'cardinal'}}}%%
+flowchart
+direction TB
+DB[(BBDD)]
+subgraph "Autenticación"
+subgraph APP["App o Web"]
+direction LR
+WEB("User
+Pass")
+end
+subgraph REQ["HTTP Request"]
+direction TB
+DT{"user
+pass"}
+JWT{JWT}
+end
+subgraph API[API REST]
+direction LR
+Aplicación
+end
+APP --"user & pass"--> DT
+JWT --"token"--> APP
+REQ <--> API
+API <--SQL--> DB
+end
+```
+
+### Hash de contraseña
+
+Al implementar una funcionalidad de autenticación en una aplicación,
+independientemente del lenguaje de programación utilizado, deberá tratar con
+los datos de inicio de sesión y contraseña de los usuarios, y deberán
+almacenarse en algún lugar, como, por ejemplo, una base de datos.
+
+Las contraseñas son información confidencial y no deben almacenarse en texto
+sin formato, ya que si una persona malintencionada logra acceder a la base de
+datos, podrá acceder a las contraseñas de todos los usuarios. Para evitar este
+problema, siempre debe usar algún algoritmo hash en las contraseñas antes de
+almacenarlas en la base de datos.
+
+**Hashing** no es más que una función matemática que convierte un texto en otro
+texto totalmente diferente y difícil de deducir. Por ejemplo, el texto *"Mi
+nombre es Rodrigo"* se puede convertir en el texto
+`8132f7cb860e9ce4c1d9062d2a5d1848`, utilizando el algoritmo ***hash MD5***.
+
+Un detalle importante es que los algoritmos de hash deben ser unidireccionales,
+es decir, no debe ser posible obtener el texto original a partir de un hash.
+Así, para saber si un usuario ingresó la contraseña correcta al intentar
+autenticarse en una aplicación, debemos tomar la contraseña que ingresó y
+generar su hash, para luego compararla con el hash que está almacenado en la
+base de datos.
+
+Hay varios algoritmos hashing que se pueden usar para transformar las contraseñas
+de los usuarios, algunos de los cuales son más antiguos y ya **no se consideran
+seguros** en la actualidad, como **MD5** y **SHA1**. Los principales algoritmos
+actualmente recomendados son:
+
+- Bcrypt
+- Scrypt
+- Argon2
+- PBKDF2
+
+Se utilizará el algoritmo **BCrypt**, que es bastante popular hoy en día. Esta
+opción también tiene en cuenta que ***Spring Security*** ya nos proporciona una
+clase que lo implementa.
+
+**Spring Data** usa su propio patrón de nomenclatura de métodos a seguir para
+que pueda generar consultas SQL correctamente.
+
+Hay algunas palabras reservadas que debemos usar en los nombres de los métodos,
+como `findBy` y `existBy`, para indicarle a **Spring Data** cómo debe ensamblar
+la consulta que queremos. Esta característica es bastante flexible y puede ser
+un poco compleja debido a las diversas posibilidades existentes.
+
+Para conocer más detalles y comprender mejor cómo ensamblar consultas dinámicas
+con Spring Data, acceda a su
+[documentación oficial](https://docs.spring.io/spring-data/jpa/docs/current/reference/html/).
+
+Bcrypt [online](https://www.browserling.com/tools/bcrypt)
+
+#### Autenticación API
+
+Se agregan las dependencias
+
+[pom.xml](./api_rest/api2/pom.xml)
+
+```xml
+ ...
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
+ ...
+```
+
+Creación de clases `Usuario`, `UsuarioRepository` y `DatosAutenticacionUsuario`
+en *package*
+[domain.usuario](./api_rest/api2/src/main/java/med/voll/api/domain/usuario/)
+
+[Usuario](./api_rest/api2/src/main/java/med/voll/api/domain/usuario/Usuario.java)
+
+```java
+@Table(name = "usuarios")
+@Entity(name = "Usuario")
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+@EqualsAndHashCode(of = "id")
+public class Usuario implements UserDetails {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+ private String login;
+ private String clave;
+
+ @Override
+ public Collection extends GrantedAuthority> getAuthorities() {
+ return List.of(new SimpleGrantedAuthority("ROLE_USER"));
+ }
+
+ @Override
+ public String getPassword() { return clave; }
+
+ @Override
+ public String getUsername() { return login; }
+
+ @Override
+ public boolean isAccountNonExpired() { return true; }
+
+ @Override
+ public boolean isAccountNonLocked() { return true; }
+
+ @Override
+ public boolean isCredentialsNonExpired() { return true; }
+
+ @Override
+ public boolean isEnabled() { return true; }
+}
+```
+
+
+[UsuarioRepository](./api_rest/api2/src/main/java/med/voll/api/domain/usuario/UsuarioRepository.java)
+
+```java
+public interface UsuarioRepository extends JpaRepository {
+ UserDetails findByLogin(String login);
+}
+```
+
+[DatosAutenticacionUsuario](./api_rest/api2/src/main/java/med/voll/api/domain/usuario/DatosAutenticacionUsuario.java)
+
+```java
+public record DatosAutenticacionUsuario(String login, String clave) {}
+```
+
+Creación de clase
+[AutenticacionController](./api_rest/api2/src/main/java/med/voll/api/controller/AutenticacionController.java)
+en *package* [controller](./api_rest/api2/src/main/java/med/voll/api/controller/)
+
+> En este punto no retorna *token*
+
+```java
+package med.voll.api.controller;
+@RestController
+@RequestMapping("/login")
+public class AutenticacionController {
+
+ @Autowired
+ private AuthenticationManager authenticationManager;
+
+ @PostMapping
+ public ResponseEntity autenticarUsuario(
+ @RequestBody @Valid DatosAutenticacionUsuario datosAutenticacionUsuario) {
+ Authentication token = new UsernamePasswordAuthenticationToken(
+ datosAutenticacionUsuario.login(),
+ datosAutenticacionUsuario.clave());
+ authenticationManager.authenticate(token);
+ return ResponseEntity.ok().build();
+
+ }
+}
+```
+
+Creación de clases `AutenticationService` y `SecurityConfigurations` en *package*
+[infra.security](./api_rest/api2/src/main/java/med/voll/api/infra/)
+
+[AutenticationService](./api_rest/api2/src/main/java/med/voll/api/infra/security/AutenticacionService.java)
+
+```java
+package med.voll.api.infra.security;
+@Service
+public class AutenticacionService implements UserDetailsService {
+
+ @Autowired
+ private UsuarioRepository usuarioRepository;
+
+ @Override
+ public UserDetails loadUserByUsername(String login)
+ throws UsernameNotFoundException {
+ return usuarioRepository.findByLogin(login);
+ }
+}
+```
+
+[SecurityConfigurations](./api_rest/api2/src/main/java/med/voll/api/infra/security/SecurityConfigurations.java)
+
+```java
+@Configuration
+@EnableWebSecurity
+public class SecurityConfigurations {
+
+ @Bean
+ public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity)
+ throws Exception {
+ return httpSecurity.csrf().disable().sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ .and().build();
+ }
+
+ @Bean
+ public AuthenticationManager authenticationManager(
+ AuthenticationConfiguration authenticationConfiguration)
+ throws Exception {
+ return authenticationConfiguration.getAuthenticationManager();
+ }
+
+ @Bean
+ public PasswordEncoder passwordEncoder () {
+ return new BCryptPasswordEncoder();
+ }
+
+}
+```
+
+Creación de nueva
+[migración](./api_rest/api2/src/main/resources/db/migration/V5__create-table-usuarios.sql)
+para crear tabla `usuarios`
+
+```sql
+create table usuarios(
+ id bigint not null auto_increment,
+ login varchar(100) not null unique,
+ clave varchar(300) not null,
+ primary key(id)
+);
+```
+