Solucionado (ver solução)

Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

Solucionado
(ver solução)
16
respostas

Token não enviado

Estou tendo o erro abaixo ao tentar realizar a requisição do login:

2024-01-12T07:49:26.937-03:00 ERROR 13280 --- [nio-8080-exec-6] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

java.lang.RuntimeException: Token JWT não enviado no cabeçalho authorization!

16 respostas

Oi!

Manda aqui o código da sua classe SecurityFilter.

Buenas!

Segue:

package com.github.paulopcrp.healthcare.api.infra.security;

import com.github.paulopcrp.healthcare.api.domain.usuario.UsuarioRepository; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; 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 {
    var tokenJWT = recuperarToken(request);

    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);
    }

    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çalho authorization!");
    }

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

}

O problema está nesse if:

if(authorizationHeader != null) {
    throw new RuntimeException("Token JWT não enviado no cabeçalho authorization!");
}

Desse jeito você está obrigando toda requisição a enviar o token, mas tem requisições em que o token não será enviado. Por exemplo, a requisição de efetuar login, pois ela será chamada para se obter um novo token.

Realizei a alteração para:

private String recuperarToken(HttpServletRequest request) {
    var authorizationHeader = request.getHeader("Authorization");
    if(authorizationHeader == null) {
        throw new RuntimeException("Token JWT não enviado no cabeçalho authorization!");
    }

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

Porem agora esta retornando

java.lang.RuntimeException: Token JWT inválido ou expirado! at com.github.paulopcrp.healthcare.api.infra.security.TokenService.getSubject(TokenService.java:47) ~[classes/:na] at com.github.paulopcrp.healthcare.api.infra.security.SecurityFilter.doFilterInternal(SecurityFilter.java:31) ~[classes/:na]

Na verdade tem que ficar assim:

private String recuperarToken(HttpServletRequest request) {
    var authorizationHeader = request.getHeader("Authorization");
    
    if(authorizationHeader != null) {
        return authorizationHeader.replace("Bearer ", "").trim();
    }

    //token não enviado na requisição:
    return null;
}

Realizei a alteração, mas ainda persiste o erro abaixo:

java.lang.RuntimeException: Token JWT inválido ou expirado! at com.github.paulopcrp.healthcare.api.infra.security.TokenService.getSubject(TokenService.java:47) ~[classes/:na] at com.github.paulopcrp.healthcare.api.infra.security.SecurityFilter.doFilterInternal(SecurityFilter.java:31) ~[classes/:na]

Agora é problema no token enviado ou algum erro na classe TokenService. Manda o código dela aqui.

Segue:

package com.github.paulopcrp.healthcare.api.infra.security;

import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTCreationException; import com.auth0.jwt.exceptions.JWTVerificationException; import com.github.paulopcrp.healthcare.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 algoritmo = Algorithm.HMAC256(secret);
        return JWT.create()
                .withIssuer("API HealthCare")
                .withSubject(usuario.getLogin())
                .withExpiresAt(dataExpeiracao())
                .sign(algoritmo);
    } catch (JWTCreationException exception){
        throw new RuntimeException("Erro ao gerar token jwt", exception);
    }
}

public String getSubject(String tokenJWT) {
    System.out.println("Recebeu token: " + tokenJWT);

    try {
        var algoritmo = Algorithm.HMAC256(secret);
        return JWT.require(algoritmo)
                .withIssuer("API HealthCare")
                .build()
                .verify(tokenJWT)
                .getSubject();

    } catch (JWTVerificationException exception){
        throw new RuntimeException("Token JWT inválido ou expirado!");
    }

}


private Instant dataExpeiracao() {

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

}

A principio está certo o código. Vi que no seu método getSubject() você colocou um system.out para imprimir o token recebido. Manda aqui um print do console com esse token que esta chegando.

Segue:

2024-01-12T09:52:10.016-03:00 INFO 23420 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms Recebeu token: undefined 2024-01-12T09:52:10.067-03:00 ERROR 23420 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

Está vindo um token inválido mesmo:

token: undefined 

Aí precisa ver como você está enviando esse token para a API, pois deve ter algum problema.

Pois é....

Não estou conseguindo fazer esse passo.

Você está enviando a requisição no Insomnia ou Postman? Manda aqui um print da requisição sendo disparada

Pelo Insomnia

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

solução!

Veja na aba Bearer. Nessa requisição de login você não deve enviar token e deve ficar assim:

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

Putz! Tava comendo bola!!

Valeu!!!