Skip to content

Commit

Permalink
feat: create user functionality for registration and login.#6
Browse files Browse the repository at this point in the history
--------
  • Loading branch information
Luciano-A1 committed Nov 21, 2024
1 parent 4436fdf commit 9377a73
Show file tree
Hide file tree
Showing 21 changed files with 679 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.web.app.config;

import com.web.app.exception.userExc.EmailNotFoundException;
import com.web.app.repository.UserRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@RequiredArgsConstructor
public class ApplicationConfig {

private final UserRepository userRepository;

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
return config.getAuthenticationManager();

}

@Bean
public AuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
authenticationProvider.setUserDetailsService(userDetailService());
authenticationProvider.setPasswordEncoder(passwordEncoder());

return authenticationProvider;
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
public UserDetailsService userDetailService() {
return email -> userRepository.findByEmail(email)
.orElseThrow(() -> new EmailNotFoundException("Usuario no encontrado con el email: " + email));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.web.app.controllers;

import com.web.app.dto.ExtendedBaseResponse;
import com.web.app.dto.user.AuthResponseDto;
import com.web.app.dto.user.LoginRequestDto;
import com.web.app.dto.user.RegisterRequestDto;
import com.web.app.service.AuthService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
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;

@Tag(name = "Autenticación", description = "Gestionar todos los puntos finales relacionados con la autenticación de usuarios.")
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class AuthController {

private final AuthService authService;

@Operation(summary = "Iniciar sesión",
description = "Autentica a un usuario con sus credenciales y devuelve un token de autenticación.")
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "Inicio de sesión exitoso.",
content = {
@Content(mediaType = "application/json",
schema = @Schema(implementation = ExtendedBaseResponse.class))
}),
@ApiResponse(responseCode = "400", description = "Credenciales inválidas proporcionadas.", content = {@Content}),
@ApiResponse(responseCode = "401", description = "No autorizado (credenciales incorrectas o expiradas).", content = {@Content}),
@ApiResponse(responseCode = "500", description = "Error del servidor.", content = {@Content})
})
@PostMapping(value = "login")
public ResponseEntity<ExtendedBaseResponse<AuthResponseDto>> login(@Valid @RequestBody LoginRequestDto request) {
ExtendedBaseResponse<AuthResponseDto> authResponse = authService.login(request);
return ResponseEntity.ok(authResponse);
}

@Operation(summary = "Registrar un nuevo usuario",
description = "Registra un nuevo usuario en el sistema con los detalles proporcionados.")
@ApiResponses(value = {
@ApiResponse(
responseCode = "200",
description = "Registro exitoso.",
content = {
@Content(mediaType = "application/json",
schema = @Schema(implementation = ExtendedBaseResponse.class))
}),
@ApiResponse(responseCode = "400", description = "El usuario ya existe o entrada no válida.", content = {@Content}),
@ApiResponse(responseCode = "500", description = "Error del servidor.", content = {@Content})
})
@PostMapping(value = "register")
public ResponseEntity<ExtendedBaseResponse<AuthResponseDto>> register(@Valid @RequestBody RegisterRequestDto request) {
ExtendedBaseResponse<AuthResponseDto> authResponse = authService.register(request);
return ResponseEntity.ok(authResponse);
}
}





Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public ResponseEntity<BaseResponse> test() {
false,
200,
"OK",
"Bienvenido a la API Rest de Reproductor de musica."
"Bienvenido a la API Rest de Reproductor de musica. Con security"
)
);
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.web.app.dto.user;

import java.io.Serializable;

public record AuthResponseDto(
Long id,
String username,
String token

) implements Serializable {
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.web.app.dto.user;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;

import java.io.Serializable;

public record LoginRequestDto(

@Email(message = "El correo electrónico debe ser valido, utilizando ´@´")
@NotBlank(message = "El correo electrónico no puede estar en blanco")
String email,

@NotBlank(message = "La contraseña no puede estar en blanco")
String password

) implements Serializable {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.web.app.dto.user;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;

import java.io.Serializable;

public record RegisterRequestDto(
@Pattern(
regexp = "^(?=\\S*[a-zA-ZÀ-ÿ])(?=(?:\\S*\\s*){3,})[a-zA-ZÀ-ÿ\\s'-]+$",
message = "El nombre de usuario debe tener al menos 3 letras y puede incluir espacios, apóstrofes o guiones"
)
@NotBlank(message = "El nombre de usuario no puede estar en blanco")
String username,

@Pattern(
regexp = "^[\\w.-]+@[\\w.-]+\\.[a-zA-Z]{2,6}$",
message = "El correo electrónico debe ser válido y contener un dominio correcto"
)
@Email(message = "El correo electrónico debe ser valido, utilizando ´@´")
@NotBlank(message = "El correo electrónico no puede estar en blanco")
String email,

@Pattern(
regexp = "^[0-9]+$",
message = "El contacto solo puede contener números positivos"
)
@NotBlank(message = "El contacto no puede estar en blanco")
String contact,

@NotBlank(message = "La contraseña no puede estar en blanco")
@Pattern(
regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$",
message = """
La contraseña debe tener al menos 8 caracteres,
contener al menos un dígito, una letra minúscula, una letra mayúscula,
un carácter especial (@#$%^&+=) y no debe tener espacios."""
)
String password

) implements Serializable {
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.web.app.exception;

import com.web.app.dto.BaseResponse;
import com.web.app.exception.userExc.EmailNotFoundException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<BaseResponse> handleIllegalArgumentException(IllegalArgumentException ex) {
return new ResponseEntity<>(new BaseResponse(true, 400, "Error", ex.getMessage()), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(EmailNotFoundException.class)
public ResponseEntity<String> handleEmailNotFound(EmailNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.web.app.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.util.HashMap;
import java.util.Map;

@ControllerAdvice
public class ValidationExceptionHandler {

@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<Map<String, String>> handleValidationExceptions(MethodArgumentNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getFieldErrors().forEach(error -> {
errors.put(error.getField(), error.getDefaultMessage());
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errors);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.web.app.exception.userExc;

import org.springframework.security.core.userdetails.UsernameNotFoundException;

public class EmailNotFoundException extends UsernameNotFoundException {
public EmailNotFoundException(String msg) {
super(msg);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.web.app.exception.userExc;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.web.app.jwt;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

private final JwtService jwtService;
private final UserDetailsService userDetailsService;

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

final String token = getTokenFromRequest(request);
final String username;

if (token == null) {
filterChain.doFilter(request, response);
return;
}

username=jwtService.getUsernameFromToken(token);

if (username!=null && SecurityContextHolder.getContext().getAuthentication()==null)
{
UserDetails userDetails=userDetailsService.loadUserByUsername(username);

if (jwtService.isTokenValid(token, userDetails))
{
UsernamePasswordAuthenticationToken authToken= new UsernamePasswordAuthenticationToken(
userDetails,
null,
userDetails.getAuthorities());

authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

SecurityContextHolder.getContext().setAuthentication(authToken);
}

}

filterChain.doFilter(request, response);
}

private String getTokenFromRequest(HttpServletRequest request) {

final String authHeader = request.getHeader(HttpHeaders.AUTHORIZATION);

if(StringUtils.hasText(authHeader) && authHeader.startsWith("Bearer ")) {

return authHeader.substring(7);

}
return authHeader;
}
}
Loading

0 comments on commit 9377a73

Please sign in to comment.