Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
Rubén Ramírez
/
MangAffinity
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
72f0cf4d
authored
Feb 21, 2025
by
Rubén Ramírez
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
feat: [TestServicioUsuario]: avances en la implementación del test de autenticación de usuario
parent
44deb578
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
138 additions
and
109 deletions
src/main/java/com/ujaen/tfg/mangaffinity/config/SecurityConfig.java
src/main/java/com/ujaen/tfg/mangaffinity/excepciones/UsuarioNoExiste.java
src/main/java/com/ujaen/tfg/mangaffinity/repositorios/RepositorioUsuario.java
src/main/java/com/ujaen/tfg/mangaffinity/seguridad/JwtUtil.java
src/main/java/com/ujaen/tfg/mangaffinity/servicios/ServicioUsuarios.java
src/test/java/com/ujaen/tfg/mangaffinity/servicios/TestServicioUsuarios.java
src/main/java/com/ujaen/tfg/mangaffinity/config/SecurityConfig.java
View file @
72f0cf4d
package
com
.
ujaen
.
tfg
.
mangaffinity
.
config
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder
;
...
...
src/main/java/com/ujaen/tfg/mangaffinity/excepciones/UsuarioNoExiste.java
0 → 100644
View file @
72f0cf4d
package
com
.
ujaen
.
tfg
.
mangaffinity
.
excepciones
;
public
class
UsuarioNoExiste
extends
RuntimeException
{}
src/main/java/com/ujaen/tfg/mangaffinity/repositorios/RepositorioUsuario.java
View file @
72f0cf4d
...
...
@@ -28,6 +28,8 @@ public class RepositorioUsuario {
em
.
persist
(
usuario
);
}
@Transactional
(
propagation
=
Propagation
.
SUPPORTS
,
readOnly
=
true
)
public
Optional
<
Usuario
>
findByEmail
(
String
email
)
{
String
query
=
"SELECT u FROM Usuario u WHERE u.email = :email"
;
...
...
src/main/java/com/ujaen/tfg/mangaffinity/seguridad/JwtUtil.java
View file @
72f0cf4d
...
...
@@ -10,71 +10,48 @@ import javax.crypto.SecretKey;
@Component
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
long
EXPIRATION_TIME
=
86400000
;
// 1 día en milisegundos
/**
* 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.
*/
// Genera un token JWT con los datos proporcionados
public
String
generateToken
(
Map
<
String
,
Object
>
claims
,
String
subject
)
{
return
Jwts
.
builder
()
.
setClaims
(
claims
)
.
setSubject
(
subject
)
.
setIssuedAt
(
new
Date
(
System
.
currentTimeMillis
()))
.
setExpiration
(
new
Date
(
System
.
currentTimeMillis
()
+
EXPIRATION_TIME
))
.
signWith
(
SECRET_KEY
)
.
setIssuedAt
(
new
Date
(
))
// Fecha de emisión
.
setExpiration
(
new
Date
(
System
.
currentTimeMillis
()
+
EXPIRATION_TIME
))
// Expiración del token
.
signWith
(
SECRET_KEY
)
// Firmar el token con la misma clave
.
compact
();
}
/**
* Obtiene el usuario (subject) desde un token JWT.
*
* @param token Token JWT.
* @return Usuario extraído del token.
*/
// Decodifica el JWT utilizando la misma clave secreta
public
Claims
decodeJWT
(
String
token
)
{
return
Jwts
.
parserBuilder
()
.
setSigningKey
(
SECRET_KEY
)
// Usamos la misma clave para decodificar
.
build
()
.
parseClaimsJws
(
token
)
.
getBody
();
}
// Extrae el nombre de usuario (subject) desde el token JWT
public
String
extractUsername
(
String
token
)
{
return
extractClaim
(
token
,
Claims:
:
getSubject
);
}
/**
* Obtiene la fecha de expiración de un token JWT.
*
* @param token Token JWT.
* @return Fecha de expiración.
*/
// Extrae la fecha de expiración del token
public
Date
extractExpiration
(
String
token
)
{
return
extractClaim
(
token
,
Claims:
:
getExpiration
);
}
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.
*/
// Extrae un claim específico del token
public
<
T
>
T
extractClaim
(
String
token
,
java
.
util
.
function
.
Function
<
Claims
,
T
>
claimsResolver
)
{
final
Claims
claims
=
extractAllClaims
(
token
);
return
claimsResolver
.
apply
(
claims
);
}
/**
* 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.
*/
// Validar si un token es correcto y no ha expirado
public
boolean
validateToken
(
String
token
,
String
username
)
{
final
String
extractedUsername
=
extractUsername
(
token
);
return
(
extractedUsername
.
equals
(
username
)
&&
!
isTokenExpired
(
token
));
...
...
@@ -86,7 +63,7 @@ public class JwtUtil {
private
Claims
extractAllClaims
(
String
token
)
{
return
Jwts
.
parserBuilder
()
.
setSigningKey
(
SECRET_KEY
)
.
setSigningKey
(
SECRET_KEY
)
// Usamos la misma clave para extraer los claims
.
build
()
.
parseClaimsJws
(
token
)
.
getBody
();
...
...
src/main/java/com/ujaen/tfg/mangaffinity/servicios/ServicioUsuarios.java
View file @
72f0cf4d
package
com
.
ujaen
.
tfg
.
mangaffinity
.
servicios
;
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.rest.DTO.DTOLoginRespuesta
;
import
com.ujaen.tfg.mangaffinity.rest.DTO.DTOUsuario
;
...
...
@@ -22,21 +23,22 @@ public class ServicioUsuarios {
RepositorioUsuario
repositorioUsuario
;
@Autowired
private
static
final
PasswordEncoder
passwordEncoder
=
new
BCryptPasswordEncoder
();
private
PasswordEncoder
passwordEncoder
;
@Autowired
private
JwtUtil
jwtUtil
;
private
static
final
Usuario
admin
;
private
final
Usuario
admin
;
static
{
// Se crea el admin con la contraseña encriptada
admin
=
new
Usuario
();
admin
.
setEmail
(
"admin@example.com"
);
admin
.
setNombreUsuario
(
"admin"
);
admin
.
setContrasenia
(
passwordEncoder
.
encode
(
"adminpassword"
));
// Contraseña encriptada
public
ServicioUsuarios
()
{
this
.
admin
=
new
Usuario
();
this
.
admin
.
setEmail
(
"admin@example.com"
);
this
.
admin
.
setNombreUsuario
(
"admin"
);
}
/**
* Función para crear un Socio en la estructura
* @param usuario usuario que se va a añadir
...
...
@@ -53,15 +55,35 @@ public class ServicioUsuarios {
* @return DTOLoginRespuesta con el token si es válido, o null si falla
*/
public
DTOLoginRespuesta
autenticarUsuario
(
String
email
,
String
contrasenia
)
{
System
.
out
.
println
(
"🔍 Intentando autenticar usuario: "
+
email
);
Optional
<
Usuario
>
usuario
=
repositorioUsuario
.
findByEmail
(
email
);
if
(
usuario
.
isEmpty
()
||
!
passwordEncoder
.
matches
(
contrasenia
,
usuario
.
get
().
getContrasenia
())
)
{
// Si el usuario no existe o la contraseña no es válida
if
(
usuario
.
isEmpty
())
{
System
.
out
.
println
(
"❌ Usuario no encontrado: "
+
email
);
return
null
;
}
// Verificamos si el usuario es el "admin"
String
rol
=
(
email
.
equals
(
admin
.
getEmail
()))
?
"ADMIN"
:
"USUARIO_REGISTRADO"
;
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
;
}
String
rol
=
"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)
Map
<
String
,
Object
>
claims
=
new
HashMap
<>();
...
...
@@ -72,8 +94,21 @@ public class ServicioUsuarios {
// Generamos el token JWT
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
());
}
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
();
}
}
src/test/java/com/ujaen/tfg/mangaffinity/servicios/TestServicioUsuarios.java
View file @
72f0cf4d
...
...
@@ -5,85 +5,97 @@ import com.ujaen.tfg.mangaffinity.config.JpaTestConfig;
import
com.ujaen.tfg.mangaffinity.entidades.Usuario
;
import
com.ujaen.tfg.mangaffinity.excepciones.UsuarioYaRegistrado
;
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.junit.jupiter.api.Test
;
import
org.slf4j.Logger
;
import
org.slf4j.LoggerFactory
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.boot.test.context.SpringBootTest
;
import
org.springframework.security.crypto.password.PasswordEncoder
;
import
org.springframework.test.annotation.DirtiesContext
;
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
.
AssertionsForClassTypes
.
assertThat
;
@SpringBootTest
(
classes
=
{
MangAffinityApplication
.
class
,
JpaTestConfig
.
class
})
@ActiveProfiles
(
"test"
)
public
class
TestServicioUsuarios
{
@Autowired
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
@DirtiesContext
void
testCrearSocio
(){
var
usuario1
=
new
Usuario
(
"pedro@gmail.com"
,
"Pedro"
,
"pedrito"
);
// Comprobamos que no se pueda meter dos socios con el mismo email
var
usuario1
=
new
Usuario
(
"pedro@gmail.com"
,
"Pedro"
,
"pedrito"
);
// Se pasa en texto plano
servicioUsuarios
.
crearUsuario
(
usuario1
);
assertThatThrownBy
(()
->
servicioUsuarios
.
crearUsuario
(
usuario1
)).
isInstanceOf
(
UsuarioYaRegistrado
.
class
);
}
/**
@Test
@DirtiesContext
void testAutenticacionUsuario() {
// Caso 1: Registrar un usuario en la base de datos en memoria
Usuario usuario = new Usuario("test@example.com", "Test User", "password123");
servicioUsuarios.crearUsuario(usuario);
void
testAutenticarUsuario
()
{
logger
.
info
(
"🔍 Iniciando test de autenticación"
);
// Caso 2: Intentar autenticar al usuario
DTOLoginRespuesta respuesta = servicioUsuarios.autenticarUsuario("test@example.com", "password123");
// Caso 1: Usuario con email incorrecto
String
emailInexistente
=
"nonexistent@example.com"
;
String
contraseniaValida
=
"validpassword"
;
assertThat
(
servicioUsuarios
.
autenticarUsuario
(
emailInexistente
,
contraseniaValida
)).
isNull
();
logger
.
info
(
"✅ Caso 1 completado: Usuario inexistente no autenticado"
);
// Verificar que se ha generado un token
Assertions.assertThat(respuesta).isNotNull();
Assertions.assertThat(respuesta.getToken()).isNotNull();
// Caso 2: Contraseña incorrecta
String
emailExistente
=
"test@example.com"
;
String
contraseniaIncorrecta
=
"wrongpassword"
;
assertThat
(
servicioUsuarios
.
autenticarUsuario
(
emailExistente
,
contraseniaIncorrecta
)).
isNull
();
logger
.
info
(
"✅ Caso 2 completado: Contraseña incorrecta no autenticada"
);
// Caso 3: Intentar autenticar con una contraseña incorrecta
DTOLoginRespuesta respuestaIncorrecta = servicioUsuarios.autenticarUsuario("test@example.com", "wrongpassword");
// Caso 3: Usuario con email y contraseña correctos
var
usuario1
=
new
Usuario
(
"pedro@gmail.com"
,
"Pedro"
,
"pedrito"
);
// Se pasa en texto plano
servicioUsuarios
.
crearUsuario
(
usuario1
);
// Verificar que no se ha generado un token
Assertions.assertThat(respuestaIncorrecta).isNull();
}
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
());
**/
@Test
@DirtiesContext
void
testAutenticarUsuario
()
{
// Caso 1: Usuario con email incorrecto
String
emailInexistente
=
"nonexistent@example.com"
;
String
contraseniaValida
=
"validpassword"
;
assertThat
(
servicioUsuarios
.
autenticarUsuario
(
emailInexistente
,
contraseniaValida
)).
isNull
();
// Caso 2: Contraseña incorrecta
String
emailExistente
=
"test@example.com"
;
// Este email debe existir en tu base de datos de prueba
String
contraseniaIncorrecta
=
"wrongpassword"
;
assertThat
(
servicioUsuarios
.
autenticarUsuario
(
emailExistente
,
contraseniaIncorrecta
)).
isNull
();
// 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
String
contraseniaCorrecta
=
"validpassword"
;
// La contraseña debe ser la correcta
DTOLoginRespuesta
respuestaValida
=
servicioUsuarios
.
autenticarUsuario
(
emailCorrecto
,
contraseniaCorrecta
);
assertThat
(
respuestaValida
).
isNotNull
();
assertThat
(
respuestaValida
.
getToken
()).
isNotNull
();
// Comprobamos que el rol de usuario registrado esté incluido en el token
assertThat
(
respuestaValida
.
getToken
()).
contains
(
"rol"
,
"USUARIO_REGISTRADO"
);
// Caso 4: Usuario ADMIN con email correcto
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"
);
}
}
DTOLoginRespuesta
respuestaValida
=
servicioUsuarios
.
autenticarUsuario
(
usuario1
.
getEmail
(),
"pedrito"
);
assertThat
(
respuestaValida
).
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
Claims
claims
=
jwtUtil
.
decodeJWT
(
respuestaValida
.
getToken
());
// Usamos la misma clave secreta para decodificar
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
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment