Solucionado (ver solução)
Solucionado
(ver solução)
10
respostas

[Dúvida] Token JWT inválido ou expirado!

Spring Boot 3: aplique boas práticas e proteja uma API Rest

Bom dia, Estou travado no projeto no pacote 05 aula 10 Testando o controle de acesso. Ao efetuar uma requisição pelo Insomnia, apresenta o erro abaixo na console.:

Chamando Filter Recebeu token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbmEuc291emFAdm9sbC5tZWQiLCJpc3MiOiJBUEkgdm9sbC5tZWQiLCJleHAiOjE3MTQwNDQ4MjJ9.W7eXFBeJcbCYcjk3Xl8JFaeH6BrlIYHyjTlVKebP3pc 2024-04-25T06:34:10.469-03:00 ERROR 2104 --- [nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

java.lang.RuntimeException: Token JWT inválido ou expirado! at med.voll.api.infra.secutity.TokenService.getSubject(TokenService.java:43) ~[classes/:na] at med.voll.api.infra.secutity.SecurityFilter.doFilterInternal(SecurityFilter.java:32) ~[classes/:na] at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Alguém pode me ajudar?

10 respostas

Oi!

Manda aqui o código completo da sua classe TokenService.

package med.voll.api.infra.secutity;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTCreationException;
import com.auth0.jwt.exceptions.JWTVerificationException;
import med.voll.api.domain.usuario.Usuario;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneOffset;

@Service
public class TokenService {

    @Value("${api.security.token.secret}")
    private String secret;
    public String gerarToken(Usuario usuario){

        try {
            var algoritimo = Algorithm.HMAC256(secret);
            return JWT.create()
                    .withIssuer("API voll.med")
                    .withSubject(usuario.getLogin())
                    .withExpiresAt(dataExpiracao())
                    .sign(algoritimo);
        }catch (JWTCreationException exception){
            throw new RuntimeException("Erro ao Gerar token jwt", exception);
        }
    }

    public String getSubject(String tokenJWT) {
        try {
            var algoritmo = Algorithm.HMAC256(secret);
            return JWT.require(algoritmo)
                    .withIssuer("API Voll.med")
                    .build()
                    .verify(tokenJWT)
                    .getSubject();
        } catch (JWTVerificationException exception) {
            throw new RuntimeException("Token JWT inválido ou expirado!");
        }
    }


    private Instant dataExpiracao() {

        return LocalDateTime.now().plusHours(2).toInstant(ZoneOffset.of("-03:00"));
    }

}

O problema está no issuer.

No método gerarToken está como API voll.med, mas no método getSubject está como API Voll.med (letra V em maiúsculo).

Uma sugestão é declarar uma constante na classe para guardar essa informação e a utilizar nos dois métodos:

private static final String ISSUER = "API Voll.med";

Rodirgo fiz o ajuste mas ainda persiste o mesmo erro

    private static final String ISSUER = "API Voll.med";
    public String gerarToken(Usuario usuario){

        try {
            var algoritimo = Algorithm.HMAC256(secret);
            return JWT.create()
                    .withIssuer("API Voll.med")
                    .withSubject(usuario.getLogin())
                    .withExpiresAt(dataExpiracao())
                    .sign(algoritimo);
        }catch (JWTCreationException exception){
            throw new RuntimeException("Erro ao Gerar token jwt", exception);
        }
    }

    public String getSubject(String tokenJWT) {
        try {
            var algoritmo = Algorithm.HMAC256(secret);
            return JWT.require(algoritmo)
                    .withIssuer("API Voll.med")
                    .build()
                    .verify(tokenJWT)
                    .getSubject();
        } catch (JWTVerificationException exception) {
            throw new RuntimeException("Token JWT inválido ou expirado!");
        }
    }

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Você precisa fazer login novamente para gerar um novo token, pois o anterior está vinculado com o issuer anterior.

Mesma erro.:

2024-04-25T09:51:23.747-03:00  INFO 16208 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 1 ms
Chamando Filter
Recebeu token: null
Hibernate: 
    select
        u1_0.id,
        u1_0.login,
        u1_0.senha 
    from
        usuarios u1_0 
    where
        u1_0.login=?
Chamando Filter
Recebeu token:  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhbmEuc291emFAdm9sbC5tZWQiLCJpc3MiOiJBUEkgVm9sbC5tZWQiLCJleHAiOjE3MTQwNTY2ODR9.Z7zqtd321gnWheHCcrlT9aW-nkswswuH9GVpaIkhmB0
2024-04-25T09:51:50.056-03:00 ERROR 16208 --- [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

java.lang.RuntimeException: Token JWT inválido ou expirado!
    at med.voll.api.infra.secutity.TokenService.getSubject(TokenService.java:45) ~[classes/:na]
    at med.voll.api.infra.secutity.SecurityFilter.doFilterInternal(SecurityFilter.java:32) ~[classes/:na]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.5.jar:6.1.5]

Manda aqui então o código da sua classe SecurityFilter, que pode ter algum problema nela

package med.voll.api.infra.secutity;

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import med.voll.api.domain.UsuarioRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class SecurityFilter extends OncePerRequestFilter {

    @Autowired
    private TokenService tokenService;
    @Autowired
    private UsuarioRepository repository;
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        System.out.println("Chamando Filter");
        var tokenJWT = recuperarToken(request);

        System.out.println("Recebeu token: " +tokenJWT);

        if (tokenJWT != null){
            var subject = tokenService.getSubject(tokenJWT);
            var usuario = repository.findByLogin(subject);

            var authentication = new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            System.out.println("logado na requisição !!");
        }
        filterChain.doFilter(request, response);
    }

    private String recuperarToken(HttpServletRequest request) {
        var authorizationHeader = request.getHeader("Authorization");
        if (authorizationHeader != null) {
            //throw new RuntimeException("Token JWT não enviado no cabeçaçho Authorization");
            return  authorizationHeader.replace("Bearer", "");
        }

        return null;
    }
}
solução!

Nesse return:

return  authorizationHeader.replace("Bearer", "");

Faltou o espaço em branco após a palavra Bearer. Correto:

return  authorizationHeader.replace("Bearer ", "");

Top....... Valeu Rodrigo

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Deu certo erra isso mesmo, não tinha espaço no "Bearer", depois que coloquei funcionou ...

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software