diff --git a/README.md b/README.md index c8b9ded..31d2f06 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,10 @@ Configurar JWT: [application.properties](https://github.com/Marc0Franc0/School-M (Se utiliza para firmar los tokens) - jwt.time.expiration = 86400000 (equivalente a un día) +Configurar usuario admin: +- user.admin.username = nombre_de_usuario_admin +- user.admin.password = contraseña_de_usuario_admin + Ejecutar localmente ```shell diff --git a/pom.xml b/pom.xml index 4ecf269..b468a0f 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ com.app User-Api-Jwt - 1.1.1 + 1.2.0 User-Api-Jwt Proyecto de muestra sobre cómo implementar la seguridad con JWT basada en Spring boot 3 y Spring security 6 @@ -47,7 +47,6 @@ org.springframework.boot spring-boot-starter-web - com.mysql mysql-connector-j @@ -68,6 +67,11 @@ spring-security-test test + + jakarta.validation + jakarta.validation-api + 3.0.2 + diff --git a/src/main/java/com/app/UserApiJwtApplication.java b/src/main/java/com/app/UserApiJwtApplication.java index f038aa7..e58dd8c 100644 --- a/src/main/java/com/app/UserApiJwtApplication.java +++ b/src/main/java/com/app/UserApiJwtApplication.java @@ -1,6 +1,7 @@ package com.app; import com.app.model.ERole; +import com.app.dto.PersonalData; import com.app.model.RoleEntity; import com.app.model.UserEntity; import com.app.repository.UserRepository; @@ -40,6 +41,8 @@ CommandLineRunner init() { .roles(Set.of(RoleEntity.builder() .name(ERole.valueOf(ERole.ADMIN.name())) .build())) + .personalData(new PersonalData + ("","",0,"")) .build(); userRepository.save(userEntity); diff --git a/src/main/java/com/app/controller/AdminController.java b/src/main/java/com/app/controller/AdminController.java index 6234c0e..2bbbf2f 100644 --- a/src/main/java/com/app/controller/AdminController.java +++ b/src/main/java/com/app/controller/AdminController.java @@ -2,6 +2,7 @@ import com.app.dto.RegisterDTO; import com.app.service.UserEntityService; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -17,7 +18,7 @@ public class AdminController { //Endpoint para poder registrarse y tener acceso como admin @PostMapping("/auth/register") - public ResponseEntity register(@RequestBody RegisterDTO registerDTO) { + public ResponseEntity register(@Valid @RequestBody RegisterDTO registerDTO) { RegisterDTO user = registerDTO; user.getRoles().clear(); user.setRoles(Set.of("ADMIN","USER")); diff --git a/src/main/java/com/app/controller/Controller.java b/src/main/java/com/app/controller/Controller.java index 8cda2cc..2753e59 100644 --- a/src/main/java/com/app/controller/Controller.java +++ b/src/main/java/com/app/controller/Controller.java @@ -2,6 +2,7 @@ import com.app.dto.RegisterDTO; import com.app.service.UserEntityService; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -16,7 +17,7 @@ public class Controller { UserEntityService userEntityService; //Endpoint para poder registrarse y tener acceso @PostMapping("/auth/register") - public ResponseEntity register(@RequestBody RegisterDTO registerDTO){ + public ResponseEntity register(@Valid @RequestBody RegisterDTO registerDTO){ RegisterDTO user = registerDTO; user.getRoles().clear(); user.setRoles(Set.of("USER")); diff --git a/src/main/java/com/app/dto/PersonalData.java b/src/main/java/com/app/dto/PersonalData.java new file mode 100644 index 0000000..cf37847 --- /dev/null +++ b/src/main/java/com/app/dto/PersonalData.java @@ -0,0 +1,11 @@ +package com.app.dto; + +import jakarta.persistence.Embeddable; + +@Embeddable +public record PersonalData(String firstName, + String lastName, + int age, + String email) { + +} diff --git a/src/main/java/com/app/dto/RegisterDTO.java b/src/main/java/com/app/dto/RegisterDTO.java index 2f93ccb..9e21ff8 100644 --- a/src/main/java/com/app/dto/RegisterDTO.java +++ b/src/main/java/com/app/dto/RegisterDTO.java @@ -1,5 +1,8 @@ package com.app.dto; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Builder; import lombok.Getter; import lombok.Setter; @@ -10,7 +13,19 @@ @Getter @Setter public class RegisterDTO { + @NotBlank(message = "username no debe estar vacío") private String username; + @NotBlank(message = "password no debe estar vacío") private String password; private Setroles; + @NotBlank(message = "firstName no debe estar vacío") + private String firstName; + @NotBlank(message = "lastName no debe estar vacío") + private String lastName; + @NotNull(message = "age no debe estar vacío") + private Integer age; + @NotBlank(message = "email no debe estar vacío") + @Email(message = "email debe tener un formato correcto") + private String email; + } diff --git a/src/main/java/com/app/exception/SQLException.java b/src/main/java/com/app/exception/SQLException.java new file mode 100644 index 0000000..713e45e --- /dev/null +++ b/src/main/java/com/app/exception/SQLException.java @@ -0,0 +1,23 @@ +package com.app.exception; + +import com.zaxxer.hikari.pool.HikariPool; +import org.hibernate.HibernateException; +import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.net.ConnectException; +import java.sql.SQLIntegrityConstraintViolationException; +@RestControllerAdvice +public class SQLException { + //Validaciones al registrarse (username duplicado en la base de datos) + @ExceptionHandler(SQLIntegrityConstraintViolationException.class) + public ResponseEntity captureSQLIntegrityConstraintViolationException + (SQLIntegrityConstraintViolationException e){ + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Message: ".concat(e.getMessage())); + } + +} diff --git a/src/main/java/com/app/exception/ValidationException.java b/src/main/java/com/app/exception/ValidationException.java new file mode 100644 index 0000000..79455b4 --- /dev/null +++ b/src/main/java/com/app/exception/ValidationException.java @@ -0,0 +1,36 @@ +package com.app.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import java.util.List; +import java.util.stream.Collectors; + +//Clase para la captura de excepciones que puedan ocurrir +@RestControllerAdvice +public class ValidationException { + //Validaciones al registrarse-> entrada de datos vacios o nulos + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity captureMethodArgumentNotValidException + (MethodArgumentNotValidException e){ + List errors = e + .getBindingResult() + .getFieldErrors() + .stream().map(FieldError::getDefaultMessage) + .collect(Collectors.toList()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("Message: ".concat(errors.toString())); + } + + //Formato JSON recibido + @ExceptionHandler( HttpMessageNotReadableException.class) + public ResponseEntity captureHttpMessageNotReadableException + (HttpMessageNotReadableException e){ + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body("Message: ".concat(e.getMessage())); + } + +} \ No newline at end of file diff --git a/src/main/java/com/app/model/Person.java b/src/main/java/com/app/model/Person.java deleted file mode 100644 index 9e13c38..0000000 --- a/src/main/java/com/app/model/Person.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.app.model; - -import lombok.Data; - -@Data -public abstract class Person { - private String firstName; - private String lastName; - private int age; - private String email; -} diff --git a/src/main/java/com/app/model/UserEntity.java b/src/main/java/com/app/model/UserEntity.java index f1a70f0..711ed87 100644 --- a/src/main/java/com/app/model/UserEntity.java +++ b/src/main/java/com/app/model/UserEntity.java @@ -1,6 +1,6 @@ package com.app.model; -import jakarta.annotation.Nullable; +import com.app.dto.PersonalData; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; @@ -8,7 +8,6 @@ import lombok.NoArgsConstructor; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.Set; @@ -23,8 +22,11 @@ public class UserEntity implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + @Column(unique = true) private String username; private String password; + @Embedded + private PersonalData personalData; @ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER) @JoinTable(name = "users_roles", joinColumns = @JoinColumn(name = "user_id"), diff --git a/src/main/java/com/app/service/UserEntityServiceImpl.java b/src/main/java/com/app/service/UserEntityServiceImpl.java index 1536c6c..c2d3a99 100644 --- a/src/main/java/com/app/service/UserEntityServiceImpl.java +++ b/src/main/java/com/app/service/UserEntityServiceImpl.java @@ -1,10 +1,12 @@ package com.app.service; +import com.app.dto.PersonalData; import com.app.dto.RegisterDTO; import com.app.model.ERole; import com.app.model.RoleEntity; import com.app.model.UserEntity; import com.app.repository.UserRepository; +import jakarta.validation.Valid; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.User; import org.springframework.security.crypto.password.PasswordEncoder; @@ -36,6 +38,12 @@ public UserEntity registerUser(RegisterDTO registerDTO){ .password(passwordEncoder.encode(registerDTO.getPassword())) //roles del usuario .roles(roles) + .personalData( new PersonalData( + registerDTO.getFirstName(), + registerDTO.getLastName(), + registerDTO.getAge(), + registerDTO.getEmail() + )) .build(); return userRepository.save(userEntity);