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 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 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) +); +``` +