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

ERRO 500

Em algum momento dessa parte do curso, Controle de Acesso, as requisições no Insomia começaram a dar erro 500:

"2025-03-26T01:13:28.223-03:00 ERROR 12676 --- [api] [nio-8080-exec-3] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.orm.jpa.JpaSystemException: No default constructor for entity 'med.voll.api.domain.medico.Medico'] with root cause

org.hibernate.InstantiationException: No default constructor for entity 'med.voll.api.domain.medico.Medico'"

Não lembro se foi a partir da configuração do Spring Security ou do JASON Web Token, mas antes estavam funcionando corretamente.

A solicitação para o Login está funcionando corretamente.

Resolvi enviar a pergunta a partir da aula "Criando o filter de segurança".

5 respostas
solução!

Oi!

Dá uma olhada aqui, pois pode ser o bug do Lombok no IntelliJ: https://cursos.alura.com.br/forum/topico-bug-unable-to-locate-constructor-for-embeddable-med-vall-api-endereco-endereco-lombok-instalado-474598

Professor, deu certo e voltou a funcionar. Porém, na aula seguinte, "Validando o token recebido", não consigo gerar um novo token, com o login, já que as duas horas configuradas já venceram, para poder fazer o teste final da aula, aparece a mensagem "Token JWT não enviado no cabeçalho Authorization!", no Insominia.

Os códigos implementados na aula:


@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 Voll.med")
                    .withSubject(usuario.getLogin())
                    .withExpiresAt(dataExpiracao())
                    .sign(algoritmo);
        } catch (JWTCreationException exception){
            throw new RuntimeException("erro ao gerrar token jwt", exception);
        }
    }

    //Valida o token
    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"));
    }
}
@Component
public class SecurityFilter extends OncePerRequestFilter {

    @Autowired
    private TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //Recupera o token do cabeçalho.
        var tokenJWT = recuperarToken(request);
        //Valida se o token está correto.
        var subject = tokenService.getSubject(tokenJWT);

        //Necessário para chamar o próximo filtro na aplicaçã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çalho Authorization!");
        }

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

Oi!

Esse seu if:

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

Você não pode lançar exception, pois nem toda requisição vai ser obrigatório enviar o token (ex: a requisição de login não envia token, pois ela é feita justamente para se obter um token).

O correto é devolver null:

if (authorizationHeader == null) {
    return null;
}

Rodrigo, fiz a mudança, mas não funcionou também, agora aparece a mensagem no Insomnia: "Token JWT inválido ou expirado!".

Esse linha "throw new RuntimeException("Token JWT não enviado no cabeçalho Authorization!");", você implementa na aula "05 Recuperando o token".

Quando fiz esse aula "05 Recuperando o token", o programa funcionou corretamente, o problema começou na aula seguinte, "06 Validando o token recebido", pois só fui estudar ela no dia seguinte, então o token já havia experido.

Porém, quando disparo uma nova solicitação de login no Insomnia, deveria surgir um novo, certo? Mas aparece a mensagem "Token JWT não enviado no cabeçalho Authorization!", e se eu alterar o código com a mudança que você sugeriu, aparece, "Token JWT inválido ou expirado!".

Reassisti as duas aulas hoje e essa parte do código que você sugeriu mudar, continua como está no meu projeto. A última aula que estudei é a "06 Validando o token recebido", nela você pode conferir esse código ainda implementado.

De qualquer forma, estou enviando o código completo das duas classes para análise. Ok?

package med.voll.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 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 algoritmo = Algorithm.HMAC256(secret);
            return JWT.create()
                    .withIssuer("API Voll.med")
                    .withSubject(usuario.getLogin())
                    .withExpiresAt(dataExpiracao())
                    .sign(algoritmo);
        } catch (JWTCreationException exception){
            throw new RuntimeException("erro ao gerrar token jwt", exception);
        }
    }

    //Valida o token
    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"));
    }
}
package med.voll.api.infra.security;

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.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;

@Component
public class SecurityFilter extends OncePerRequestFilter {

    @Autowired
    private TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //Recupera o token do cabeçalho.
        var tokenJWT = recuperarToken(request);
        //Valida se o token está correto.
        var subject = tokenService.getSubject(tokenJWT);

        //Necessário para chamar o próximo filtro na aplicaçã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çalho Authorization!");
        }

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

Oie! O código deve ficar assim:

@Component
public class SecurityFilter extends OncePerRequestFilter {

    @Autowired
    private TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //Recupera o token do cabeçalho.
        var tokenJWT = recuperarToken(request);
        
        if (tokenJWT != null) {
            var subject = tokenService.getSubject(tokenJWT);
        }

        //Necessário para chamar o próximo filtro na aplicação.
        filterChain.doFilter(request, response);
    }

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

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