1
respuesta

[Proyecto] Error de que verifier llega el subject null al verifier de la clase TokenService.

Hola buena noche, saben tengo el problema que cuando el subject del token llega al TokenService, si usted llega con el valor de null, y no sé por qué pasa eso estuve investigando acerca del error de qué es lo que genera que obtenga un valor de null : Así que se generó la duda si en este código era que se definía el issuer:

   verifier = JWT.require(algorithm)
                    .withIssuer("Resumes")
                    .build()
                    .verify(token);
            verifier.getSubject();

Y entre las posibilidades que encontré fueron estos: Firma inválida: Si la firma del token no coincide con la firma esperada según el algoritmo y el secreto proporcionado, se lanzará una excepción y verifier quedará como nulo. Token expirado, Issuer no válido, Token no válido en general,

Así están el código proyecto: Esta es la clase AuthenticationService:

package rsm.ces.api.infra.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import rsm.ces.api.domain.usuarios.UsuarioRepository;

@Service
public class AuthenticationService implements UserDetailsService {

    @Autowired
    private UsuarioRepository usuarioRepository;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return usuarioRepository.findByUser(username);
    }
}

Esta es la clase SecurityFilter:

package rsm.ces.api.infra.security;

@Component
public class SecurityFilter extends OncePerRequestFilter {

    @Autowired
    private TokenService tokenService;
    @Autowired
    private UsuarioRepository usuarioRepository;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        // Obtener el token del header
        var authHeader = request.getHeader("Authorization");
        if (authHeader != null) {
            var token = authHeader.replace("Bearer ", "");
            var nombreUsuario = tokenService.getSubject(token); // extract username
            if (nombreUsuario != null) {
                // Token valido
                var usuario = usuarioRepository.findByUser(nombreUsuario);
                var authentication = new UsernamePasswordAuthenticationToken(usuario, null,
                        usuario.getAuthorities()); // Forzamos un inicio de sesion
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        filterChain.doFilter(request, response);
    }
}

Esta es la clase TokenService:

package rsm.ces.api.infra.security;

@Service
public class TokenService {

    @Value("${api.security.secret}")
    private String apiSecret;
    public String generateToken(Usuario usuario){
        try {
            Algorithm algorithm = Algorithm.HMAC256(apiSecret);
             return JWT.create()
                    .withIssuer("Resumes")
                     .withSubject(usuario.getUser())
                     .withClaim("id", usuario.getId())
                     .withExpiresAt(generateExpirationDate())
                    .sign(algorithm);
        } catch (JWTCreationException exception){
            throw new RuntimeException();
        }
    }

    public String getSubject(String token) {
        if (token == null) {
            throw new RuntimeException();
        }
        DecodedJWT verifier = null;
        try {
            Algorithm algorithm = Algorithm.HMAC256(apiSecret); // validando firma
            verifier = JWT.require(algorithm)
                    .withIssuer("Resumes")
                    .build()
                    .verify(token);
            verifier.getSubject();
        } catch (JWTVerificationException exception) {
            System.out.println("Error al verificar el token: " + exception.getMessage());
            System.out.println(exception.toString());
        }
        if (verifier.getSubject() == null) {
            throw new RuntimeException("Verifier invalido");
        }
        return verifier.getSubject();
    }

    private Instant generateExpirationDate(){
        return LocalDateTime.now().plusHours(2).toInstant(ZoneOffset.of("-06:00"));
    }

}

Ya que lo que he encontrado son maneras para validar de que el subject no sea null, pero el problema es que llega null a la clase TokenService, pero no sé cómo llenarlo con el valor real que no sea nulo. Espero que alguien me pueda ayudar por favor. Les agradezco de antemano.

1 respuesta

Hola César,

Gracias por compartir tu duda con nosotros. Sí, el problema puede estar relacionado con la verificación del token y la obtención del subject.

En la clase TokenService, estás usando la librería JWT para verificar el token y obtener el subject. Sin embargo, en caso de que ocurra una excepción durante la verificación, no estás manejando correctamente el valor del verifier, lo cual puede resultar en un valor nulo para el subject.

public String getSubject(String token) {
    if (token == null) {
        throw new RuntimeException("Token nulo");
    }
    try {
        Algorithm algorithm = Algorithm.HMAC256(apiSecret); // validando firma
        DecodedJWT verifier = JWT.require(algorithm)
                .withIssuer("Resumes")
                .build()
                .verify(token);
        return verifier.getSubject();
    } catch (JWTVerificationException exception) {
        throw new RuntimeException("Error al verificar el token: " + exception.getMessage());
    }
}

porque si ocurre una excepción durante la verificación del token, se lanzará una RuntimeException con un mensaje de error específico. Esto te permitirá identificar y solucionar el problema más fácilmente.

Espero haber ayudado y buenos estudios!