Commit 3c7dca7a by Rubén Ramírez

fix: [*]: Corregidos unos bugs detectados

parent 3c4635e0
package com.ujaen.tfg.mangaffinity.excepciones;
public class NombreUsuarioYaCogido extends RuntimeException {
}
package com.ujaen.tfg.mangaffinity.repositorios; package com.ujaen.tfg.mangaffinity.repositorios;
import com.ujaen.tfg.mangaffinity.entidades.Usuario; import com.ujaen.tfg.mangaffinity.entidades.Usuario;
import com.ujaen.tfg.mangaffinity.excepciones.NombreUsuarioYaCogido;
import com.ujaen.tfg.mangaffinity.excepciones.UsuarioYaRegistrado; import com.ujaen.tfg.mangaffinity.excepciones.UsuarioYaRegistrado;
import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext; import jakarta.persistence.PersistenceContext;
...@@ -18,13 +19,26 @@ public class RepositorioUsuario { ...@@ -18,13 +19,26 @@ public class RepositorioUsuario {
public void guardar(Usuario usuario) { public void guardar(Usuario usuario) {
if (!em.createQuery("select u from Usuario u where u.email = ?1", Usuario.class) // Email no registrados
.setParameter(1, usuario.getEmail()) boolean emailExistente = !em.createQuery("SELECT u FROM Usuario u WHERE u.email = :email", Usuario.class)
.setParameter("email", usuario.getEmail())
.getResultList() .getResultList()
.isEmpty()) { .isEmpty();
if (emailExistente) {
throw new UsuarioYaRegistrado(); throw new UsuarioYaRegistrado();
} }
//Nombre de usuario no utilizado
boolean nombreUsuarioExistente = !em.createQuery("SELECT u FROM Usuario u WHERE u.nombreUsuario = :nombreUsuario", Usuario.class)
.setParameter("nombreUsuario", usuario.getNombreUsuario())
.getResultList()
.isEmpty();
if (nombreUsuarioExistente) {
throw new NombreUsuarioYaCogido();
}
em.persist(usuario); em.persist(usuario);
} }
......
...@@ -6,7 +6,6 @@ import lombok.Getter; ...@@ -6,7 +6,6 @@ import lombok.Getter;
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public class DTOLoginRespuesta { public class DTOLoginRespuesta {
private String token; private String token;
private String email; private String email;
private String nombreUsuario; private String nombreUsuario;
......
...@@ -13,9 +13,7 @@ import lombok.Setter; ...@@ -13,9 +13,7 @@ import lombok.Setter;
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public class DTOUsuario { public class DTOUsuario {
private Long id = null; // Permito que sea nulo al crear
private Long id = null; // Permitir que sea nulo al crear
@Email @Email
private String email; private String email;
......
...@@ -10,12 +10,12 @@ import javax.crypto.SecretKey; ...@@ -10,12 +10,12 @@ import javax.crypto.SecretKey;
@Component @Component
public class JwtUtil { public class JwtUtil {
// Clave secreta para firmar y verificar el token (debe ser la misma en todo el proyecto)
private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256); private static final SecretKey SECRET_KEY = Keys.secretKeyFor(SignatureAlgorithm.HS256);
private static final long EXPIRATION_TIME = 86400000; // 1 día en milisegundos private static final long EXPIRATION_TIME = 86400000; // 1 día en milisegundos
// Genera un token JWT con los datos proporcionados // Genera un token JWT con los datos
public String generateToken(Map<String, Object> claims, String subject) { public String generateToken(Map<String, Object> claims, String subject) {
return Jwts.builder() return Jwts.builder()
.setClaims(claims) .setClaims(claims)
...@@ -26,7 +26,7 @@ public class JwtUtil { ...@@ -26,7 +26,7 @@ public class JwtUtil {
.compact(); .compact();
} }
// Decodifica el JWT utilizando la misma clave secreta // Decodifica el JWT
public Claims decodeJWT(String token) { public Claims decodeJWT(String token) {
return Jwts.parserBuilder() return Jwts.parserBuilder()
.setSigningKey(SECRET_KEY) // Usamos la misma clave para decodificar .setSigningKey(SECRET_KEY) // Usamos la misma clave para decodificar
...@@ -35,23 +35,22 @@ public class JwtUtil { ...@@ -35,23 +35,22 @@ public class JwtUtil {
.getBody(); .getBody();
} }
// Extrae el nombre de usuario (subject) desde el token JWT // Extrae el nombre de usuario
public String extractUsername(String token) { public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject); return extractClaim(token, Claims::getSubject);
} }
// Extrae la fecha de expiración del token // Extrae la fecha de expiración
public Date extractExpiration(String token) { public Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration); return extractClaim(token, Claims::getExpiration);
} }
// Extrae un claim específico del token // Extrae un claim específico
public <T> T extractClaim(String token, java.util.function.Function<Claims, T> claimsResolver) { public <T> T extractClaim(String token, java.util.function.Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token); final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims); return claimsResolver.apply(claims);
} }
// Validar si un token es correcto y no ha expirado
public boolean validateToken(String token, String username) { public boolean validateToken(String token, String username) {
final String extractedUsername = extractUsername(token); final String extractedUsername = extractUsername(token);
return (extractedUsername.equals(username) && !isTokenExpired(token)); return (extractedUsername.equals(username) && !isTokenExpired(token));
......
...@@ -3,13 +3,12 @@ package com.ujaen.tfg.mangaffinity.seguridad; ...@@ -3,13 +3,12 @@ package com.ujaen.tfg.mangaffinity.seguridad;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod; import org.springframework.http.HttpMethod;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager;
@Configuration @Configuration
@EnableWebSecurity @EnableWebSecurity
......
...@@ -18,15 +18,12 @@ import java.util.Optional; ...@@ -18,15 +18,12 @@ import java.util.Optional;
@Service @Service
public class ServicioUsuarios { public class ServicioUsuarios {
@Autowired @Autowired
RepositorioUsuario repositorioUsuario; RepositorioUsuario repositorioUsuario;
@Autowired @Autowired
private PasswordEncoder passwordEncoder; private PasswordEncoder passwordEncoder;
@Autowired @Autowired
private JwtUtil jwtUtil; private JwtUtil jwtUtil;
...@@ -38,7 +35,6 @@ public class ServicioUsuarios { ...@@ -38,7 +35,6 @@ public class ServicioUsuarios {
this.admin.setNombreUsuario("admin"); this.admin.setNombreUsuario("admin");
} }
/** /**
* Función para crear un Socio en la estructura * Función para crear un Socio en la estructura
* @param usuario usuario que se va a añadir * @param usuario usuario que se va a añadir
...@@ -55,36 +51,23 @@ public class ServicioUsuarios { ...@@ -55,36 +51,23 @@ public class ServicioUsuarios {
* @return DTOLoginRespuesta con el token si es válido, o null si falla * @return DTOLoginRespuesta con el token si es válido, o null si falla
*/ */
public DTOLoginRespuesta autenticarUsuario(String email, String contrasenia) { public DTOLoginRespuesta autenticarUsuario(String email, String contrasenia) {
System.out.println("🔍 Intentando autenticar usuario: " + email);
Optional<Usuario> usuario = repositorioUsuario.findByEmail(email); Optional<Usuario> usuario = repositorioUsuario.findByEmail(email);
if (usuario.isEmpty()) { if (usuario.isEmpty()) {
System.out.println("❌ Usuario no encontrado: " + email);
return null; return null;
} }
System.out.println("✅ Usuario encontrado: " + usuario.get().getEmail());
System.out.println("Contraseña almacenada (encriptada): " + usuario.get().getContrasenia());
System.out.println("Contraseña ingresada: " + contrasenia);
System.out.println("¿Coincide? " + passwordEncoder.matches(contrasenia, usuario.get().getContrasenia()));
if (!passwordEncoder.matches(contrasenia, usuario.get().getContrasenia())) { if (!passwordEncoder.matches(contrasenia, usuario.get().getContrasenia())) {
System.out.println("❌ Contraseña incorrecta para: " + email);
return null; return null;
} }
String rol = "USUARIO_REGISTRADO"; String rol = "USUARIO_REGISTRADO";
if (email.equals(admin.getEmail())) { if (email.equals(admin.getEmail())) {
if (!passwordEncoder.matches(contrasenia, passwordEncoder.encode("adminpassword"))) { if (!passwordEncoder.matches(contrasenia, passwordEncoder.encode("adminpassword"))) {
System.out.println("❌ Contraseña incorrecta para admin");
return null; return null;
} }
rol = "ADMIN"; rol = "ADMIN";
} }
// Datos adicionales en el token (claims) // Datos adicionales en el token (claims)
Map<String, Object> claims = new HashMap<>(); Map<String, Object> claims = new HashMap<>();
claims.put("email", usuario.get().getEmail()); claims.put("email", usuario.get().getEmail());
...@@ -93,9 +76,6 @@ public class ServicioUsuarios { ...@@ -93,9 +76,6 @@ public class ServicioUsuarios {
// Generamos el token JWT // Generamos el token JWT
String token = jwtUtil.generateToken(claims, usuario.get().getEmail()); String token = jwtUtil.generateToken(claims, usuario.get().getEmail());
System.out.println("✅ Usuario autenticado exitosamente. Token generado.");
return new DTOLoginRespuesta(token, usuario.get().getEmail(), usuario.get().getContrasenia()); return new DTOLoginRespuesta(token, usuario.get().getEmail(), usuario.get().getContrasenia());
} }
......
...@@ -69,7 +69,7 @@ public class TestUsuariosController { ...@@ -69,7 +69,7 @@ public class TestUsuariosController {
Assertions.assertThat(usuarioCreado.getId()).isNotNull(); Assertions.assertThat(usuarioCreado.getId()).isNotNull();
// Caso 3: Intentar registrar un usuario con el mismo email // Caso 3: Intentar registrar un usuario con el mismo email
var usuarioDuplicado = new DTOUsuario(null, "carlitos@gmail.com", "Carlitos", "password123"); var usuarioDuplicado = new DTOUsuario(null, "pedro@gmail.com", "Pedro", "pedrito");
var respuestaDuplicado = restTemplateUsuarios.postForEntity( var respuestaDuplicado = restTemplateUsuarios.postForEntity(
"/", "/",
usuarioDuplicado, usuarioDuplicado,
......
...@@ -7,26 +7,19 @@ import com.ujaen.tfg.mangaffinity.excepciones.UsuarioYaRegistrado; ...@@ -7,26 +7,19 @@ import com.ujaen.tfg.mangaffinity.excepciones.UsuarioYaRegistrado;
import com.ujaen.tfg.mangaffinity.rest.DTO.DTOLoginRespuesta; import com.ujaen.tfg.mangaffinity.rest.DTO.DTOLoginRespuesta;
import com.ujaen.tfg.mangaffinity.seguridad.JwtUtil; import com.ujaen.tfg.mangaffinity.seguridad.JwtUtil;
import io.jsonwebtoken.Claims; import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.ActiveProfiles;
import javax.crypto.SecretKey;
import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat; import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
@SpringBootTest(classes = {MangAffinityApplication.class, JpaTestConfig.class}) @SpringBootTest(classes = {MangAffinityApplication.class, JpaTestConfig.class})
@ActiveProfiles("test") @ActiveProfiles("test")
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class TestServicioUsuarios { public class TestServicioUsuarios {
@Autowired @Autowired
...@@ -48,37 +41,29 @@ public class TestServicioUsuarios { ...@@ -48,37 +41,29 @@ public class TestServicioUsuarios {
@Test @Test
@DirtiesContext @DirtiesContext
void testAutenticarUsuario() { void testAutenticarUsuario() {
logger.info("🔍 Iniciando test de autenticación");
// Caso 1: Usuario con email incorrecto // Caso 1: Usuario con email incorrecto
String emailInexistente = "nonexistent@example.com"; String emailInexistente = "nonexistent@example.com";
String contraseniaValida = "validpassword"; String contraseniaValida = "validpassword";
assertThat(servicioUsuarios.autenticarUsuario(emailInexistente, contraseniaValida)).isNull(); assertThat(servicioUsuarios.autenticarUsuario(emailInexistente, contraseniaValida)).isNull();
logger.info("✅ Caso 1 completado: Usuario inexistente no autenticado");
// Caso 2: Contraseña incorrecta // Caso 2: Contraseña incorrecta
String emailExistente = "test@example.com"; String emailExistente = "test@example.com";
String contraseniaIncorrecta = "wrongpassword"; String contraseniaIncorrecta = "wrongpassword";
assertThat(servicioUsuarios.autenticarUsuario(emailExistente, contraseniaIncorrecta)).isNull(); assertThat(servicioUsuarios.autenticarUsuario(emailExistente, contraseniaIncorrecta)).isNull();
logger.info("✅ Caso 2 completado: Contraseña incorrecta no autenticada");
// Caso 3: Usuario con email y contraseña correctos // Caso 3: Usuario con email y contraseña correctos
var usuario1 = new Usuario("pedro@gmail.com", "Pedro", "pedrito"); // Se pasa en texto plano var usuario2 = new Usuario("pedra@gmail.com", "Pedra", "pedrito"); // Se pasa en texto plano
servicioUsuarios.crearUsuario(usuario1); servicioUsuarios.crearUsuario(usuario2);
Usuario usuarioGuardado = servicioUsuarios.buscaUsuario("pedro@gmail.com"); Usuario usuarioGuardado = servicioUsuarios.buscaUsuario("pedro@gmail.com");
assertThat(usuarioGuardado).isNotNull(); assertThat(usuarioGuardado).isNotNull();
logger.info("✅ Usuario guardado en BD: {}", usuarioGuardado.getEmail());
logger.info("Contraseña en BD (encriptada): {}", usuarioGuardado.getContrasenia());
DTOLoginRespuesta respuestaValida = servicioUsuarios.autenticarUsuario(usuario1.getEmail(), "pedrito"); DTOLoginRespuesta respuestaValida = servicioUsuarios.autenticarUsuario(usuario2.getEmail(), "pedrito");
assertThat(respuestaValida).isNotNull(); assertThat(respuestaValida).isNotNull();
assertThat(respuestaValida.getToken()).isNotNull(); assertThat(respuestaValida.getToken()).isNotNull();
logger.info("✅ Caso 3 completado: Usuario autenticado correctamente");
// Comprobamos que el rol de usuario registrado esté incluido en el token // Comprobamos que el rol de usuario registrado esté incluido en el token
Claims claims = jwtUtil.decodeJWT(respuestaValida.getToken()); // Usamos la misma clave secreta para decodificar Claims claims = jwtUtil.decodeJWT(respuestaValida.getToken()); // Usamos la misma clave secreta para decodificar
assertThat(claims.get("rol")).isEqualTo("USUARIO_REGISTRADO"); assertThat(claims.get("rol")).isEqualTo("USUARIO_REGISTRADO");
logger.info("✅ El rol de usuario registrado está presente en el token");
} }
} }
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment