Commit 72f0cf4d by Rubén Ramírez

feat: [TestServicioUsuario]: avances en la implementación del test de autenticación de usuario

parent 44deb578
package com.ujaen.tfg.mangaffinity.config; package com.ujaen.tfg.mangaffinity.config;
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.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
......
package com.ujaen.tfg.mangaffinity.excepciones;
public class UsuarioNoExiste extends RuntimeException {}
...@@ -28,6 +28,8 @@ public class RepositorioUsuario { ...@@ -28,6 +28,8 @@ public class RepositorioUsuario {
em.persist(usuario); em.persist(usuario);
} }
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true) @Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public Optional<Usuario> findByEmail(String email) { public Optional<Usuario> findByEmail(String email) {
String query = "SELECT u FROM Usuario u WHERE u.email = :email"; String query = "SELECT u FROM Usuario u WHERE u.email = :email";
......
...@@ -10,71 +10,48 @@ import javax.crypto.SecretKey; ...@@ -10,71 +10,48 @@ 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 proporcionados.
*
* @param claims Información adicional a incluir en el token.
* @param subject Usuario o identificador asociado al token.
* @return Token JWT generado.
*/
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)
.setSubject(subject) .setSubject(subject)
.setIssuedAt(new Date(System.currentTimeMillis())) .setIssuedAt(new Date()) // Fecha de emisión
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) // Expiración del token
.signWith(SECRET_KEY) .signWith(SECRET_KEY) // Firmar el token con la misma clave
.compact(); .compact();
} }
/** // Decodifica el JWT utilizando la misma clave secreta
* Obtiene el usuario (subject) desde un token JWT. public Claims decodeJWT(String token) {
* return Jwts.parserBuilder()
* @param token Token JWT. .setSigningKey(SECRET_KEY) // Usamos la misma clave para decodificar
* @return Usuario extraído del token. .build()
*/ .parseClaimsJws(token)
.getBody();
}
// Extrae el nombre de usuario (subject) desde el token JWT
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
* Obtiene la fecha de expiración de un token JWT.
*
* @param token Token JWT.
* @return 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
public String extractRoles(String token) {
Claims claims = extractAllClaims(token);
return (String) claims.get("roles"); // Extrae el rol desde los claims
}
/**
* Extrae un claim específico del token.
*
* @param token Token JWT.
* @param claimsResolver Función que extrae el claim.
* @return Valor del claim extraído.
*/
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
* Valida si un token es correcto y no ha expirado.
*
* @param token Token JWT.
* @param username Nombre de usuario a validar.
* @return `true` si el token es válido.
*/
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));
...@@ -86,7 +63,7 @@ public class JwtUtil { ...@@ -86,7 +63,7 @@ public class JwtUtil {
private Claims extractAllClaims(String token) { private Claims extractAllClaims(String token) {
return Jwts.parserBuilder() return Jwts.parserBuilder()
.setSigningKey(SECRET_KEY) .setSigningKey(SECRET_KEY) // Usamos la misma clave para extraer los claims
.build() .build()
.parseClaimsJws(token) .parseClaimsJws(token)
.getBody(); .getBody();
......
package com.ujaen.tfg.mangaffinity.servicios; package com.ujaen.tfg.mangaffinity.servicios;
import com.ujaen.tfg.mangaffinity.entidades.Usuario; import com.ujaen.tfg.mangaffinity.entidades.Usuario;
import com.ujaen.tfg.mangaffinity.excepciones.UsuarioNoExiste;
import com.ujaen.tfg.mangaffinity.repositorios.RepositorioUsuario; import com.ujaen.tfg.mangaffinity.repositorios.RepositorioUsuario;
import com.ujaen.tfg.mangaffinity.rest.DTO.DTOLoginRespuesta; import com.ujaen.tfg.mangaffinity.rest.DTO.DTOLoginRespuesta;
import com.ujaen.tfg.mangaffinity.rest.DTO.DTOUsuario; import com.ujaen.tfg.mangaffinity.rest.DTO.DTOUsuario;
...@@ -22,21 +23,22 @@ public class ServicioUsuarios { ...@@ -22,21 +23,22 @@ public class ServicioUsuarios {
RepositorioUsuario repositorioUsuario; RepositorioUsuario repositorioUsuario;
@Autowired @Autowired
private static final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); private PasswordEncoder passwordEncoder;
@Autowired @Autowired
private JwtUtil jwtUtil; private JwtUtil jwtUtil;
private static final Usuario admin; private final Usuario admin;
static { public ServicioUsuarios() {
// Se crea el admin con la contraseña encriptada this.admin = new Usuario();
admin = new Usuario(); this.admin.setEmail("admin@example.com");
admin.setEmail("admin@example.com"); this.admin.setNombreUsuario("admin");
admin.setNombreUsuario("admin");
admin.setContrasenia(passwordEncoder.encode("adminpassword")); // Contraseña encriptada
} }
/** /**
* 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
...@@ -53,15 +55,35 @@ public class ServicioUsuarios { ...@@ -53,15 +55,35 @@ 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() || !passwordEncoder.matches(contrasenia, usuario.get().getContrasenia())) { if (usuario.isEmpty()) {
// Si el usuario no existe o la contraseña no es válida System.out.println("❌ Usuario no encontrado: " + email);
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())) {
System.out.println("❌ Contraseña incorrecta para: " + email);
return null; return null;
} }
// Verificamos si el usuario es el "admin" String rol = "USUARIO_REGISTRADO";
String rol = (email.equals(admin.getEmail())) ? "ADMIN" : "USUARIO_REGISTRADO";
if (email.equals(admin.getEmail())) {
if (!passwordEncoder.matches(contrasenia, passwordEncoder.encode("adminpassword"))) {
System.out.println("❌ Contraseña incorrecta para admin");
return null;
}
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<>();
...@@ -72,8 +94,21 @@ public class ServicioUsuarios { ...@@ -72,8 +94,21 @@ 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());
} }
public Usuario buscaUsuario(String email){
if (email.equals(admin.getEmail()))
return admin;
Optional<Usuario> usuario = repositorioUsuario.findByEmail(email);
if(usuario.isPresent())
return usuario.get();
throw new UsuarioNoExiste();
}
} }
...@@ -5,85 +5,97 @@ import com.ujaen.tfg.mangaffinity.config.JpaTestConfig; ...@@ -5,85 +5,97 @@ import com.ujaen.tfg.mangaffinity.config.JpaTestConfig;
import com.ujaen.tfg.mangaffinity.entidades.Usuario; import com.ujaen.tfg.mangaffinity.entidades.Usuario;
import com.ujaen.tfg.mangaffinity.excepciones.UsuarioYaRegistrado; 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 io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.assertj.core.api.Assertions; 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.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 java.nio.charset.StandardCharsets;
import java.security.Key;
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")
public class TestServicioUsuarios { public class TestServicioUsuarios {
@Autowired @Autowired
ServicioUsuarios servicioUsuarios; ServicioUsuarios servicioUsuarios;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
JwtUtil jwtUtil;
private static final Logger logger = LoggerFactory.getLogger(TestServicioUsuarios.class);
private static final SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256); // Clave de 256 bits
private Claims decodeJWT(String token) {
return Jwts.parserBuilder()
.setSigningKey(key) // Usando la misma clave
.build()
.parseClaimsJws(token)
.getBody();
}
@Test @Test
@DirtiesContext @DirtiesContext
void testCrearSocio(){ void testCrearSocio(){
var usuario1 = new Usuario("pedro@gmail.com", "Pedro", "pedrito"); var usuario1 = new Usuario("pedro@gmail.com", "Pedro", "pedrito"); // Se pasa en texto plano
// Comprobamos que no se pueda meter dos socios con el mismo email
servicioUsuarios.crearUsuario(usuario1); servicioUsuarios.crearUsuario(usuario1);
assertThatThrownBy(() -> servicioUsuarios.crearUsuario(usuario1)).isInstanceOf(UsuarioYaRegistrado.class); assertThatThrownBy(() -> servicioUsuarios.crearUsuario(usuario1)).isInstanceOf(UsuarioYaRegistrado.class);
} }
/**
@Test @Test
@DirtiesContext @DirtiesContext
void testAutenticacionUsuario() { void testAutenticarUsuario() {
// Caso 1: Registrar un usuario en la base de datos en memoria logger.info("🔍 Iniciando test de autenticación");
Usuario usuario = new Usuario("test@example.com", "Test User", "password123");
servicioUsuarios.crearUsuario(usuario);
// Caso 2: Intentar autenticar al usuario
DTOLoginRespuesta respuesta = servicioUsuarios.autenticarUsuario("test@example.com", "password123");
// Verificar que se ha generado un token
Assertions.assertThat(respuesta).isNotNull();
Assertions.assertThat(respuesta.getToken()).isNotNull();
// Caso 3: Intentar autenticar con una contraseña incorrecta
DTOLoginRespuesta respuestaIncorrecta = servicioUsuarios.autenticarUsuario("test@example.com", "wrongpassword");
// Verificar que no se ha generado un token
Assertions.assertThat(respuestaIncorrecta).isNull();
}
**/
@Test
@DirtiesContext
void testAutenticarUsuario() {
// 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"; // Este email debe existir en tu base de datos de prueba 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
String emailCorrecto = "test@example.com"; // Este email debe existir en tu base de datos de prueba var usuario1 = new Usuario("pedro@gmail.com", "Pedro", "pedrito"); // Se pasa en texto plano
String contraseniaCorrecta = "validpassword"; // La contraseña debe ser la correcta servicioUsuarios.crearUsuario(usuario1);
DTOLoginRespuesta respuestaValida = servicioUsuarios.autenticarUsuario(emailCorrecto, contraseniaCorrecta);
Usuario usuarioGuardado = servicioUsuarios.buscaUsuario("pedro@gmail.com");
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");
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
assertThat(respuestaValida.getToken()).contains("rol", "USUARIO_REGISTRADO"); Claims claims = jwtUtil.decodeJWT(respuestaValida.getToken()); // Usamos la misma clave secreta para decodificar
assertThat(claims.get("rol")).isEqualTo("USUARIO_REGISTRADO");
// Caso 4: Usuario ADMIN con email correcto logger.info("✅ El rol de usuario registrado está presente en el token");
String emailAdmin = "admin@example.com"; // Este debe ser el email del admin }
String contraseniaAdmin = "adminpassword"; // Contraseña encriptada para el admin
DTOLoginRespuesta respuestaAdmin = servicioUsuarios.autenticarUsuario(emailAdmin, contraseniaAdmin);
assertThat(respuestaAdmin).isNotNull();
assertThat(respuestaAdmin.getToken()).isNotNull();
// Comprobamos que el rol ADMIN esté presente en el token
assertThat(respuestaAdmin.getToken()).contains("rol", "ADMIN");
}
} }
\ 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