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

[ERRO] The token was expected to have 3 parts, but got 0.

Professor, desculpa mais uma ver abrir uma pergunta, mais agora estou recebendo esse erro -> The token was expected to have 3 parts, but got 0. se eu uso o getSubject, se eu o desabilito consigo gerar o login e pegar o token, porém se uso o getSubject ele não valida o subject.

SecurityFilter

@Component
public class SecurityFilter extends OncePerRequestFilter {

    @Autowired
    private TokenService tokenService;

    @Autowired
    private RepositorioUsuarios repositorioUsuario;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        var tokenJWT = retrieveToken(request);
        if(tokenJWT != null){
            var subject = tokenService.getSubject(tokenJWT);
            var usuario = repositorioUsuario.findByUsername(subject);
            SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken
                    (usuario, null, usuario.getAuthorities()));
        }
        filterChain.doFilter(request, response);
    }

    private String retrieveToken(HttpServletRequest request) {
        var authorizationHeader = request.getHeader("Authorization");

        if(authorizationHeader != null){
            return authorizationHeader.replace("Bearer ","");
        }
        return "Token não recuperado";
    }

}
13 respostas
TokenService

@Service
public class TokenService {

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

    private static final String ISSUER = "API Projeto.Escola";

    public String tokenGenerator(Usuarios usuarios){
        try {
            var algorithm = Algorithm.HMAC256(secret);
            return  JWT.create()
                    .withIssuer(ISSUER)
                    .withSubject(usuarios.getUsername())
                    .withClaim("id", usuarios.getId())
                    .withClaim("username", usuarios.getUsername())
                    .withClaim("rolesUsuarios", String.valueOf(usuarios.getRolesUsuarios()))
                    .withExpiresAt(dataExpiracao())
                    //.withExpiresAt(expirationData())
                    .sign(algorithm);
        } catch (JWTCreationException exception){
            // Invalid Signing configuration / Couldn't convert Claims.
            throw new RuntimeException("Erro ao gerar o token", exception);
        }
    }
    public String getSubject(String tokenJWT){
        try {
            var algorithm = Algorithm.HMAC256(secret);
            return JWT.require(algorithm)
                    .withIssuer(ISSUER)
                    .build()
                    .verify(tokenJWT)
                    .getSubject();
        } catch (JWTVerificationException exception) {
            exception.printStackTrace();
            throw new RuntimeException("Token JWT inválido ou expirado!");
        }
    }
    private Instant dataExpiracao() {
        return LocalDateTime.now().plusHours(2).toInstant(ZoneOffset.of("-03:00"));
    }


}

Fui verificar até o token no jwt.io

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

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

Oi Bruno!

Manda um print da requisição no Insmonia. Verifica como o token está sendo enviado

O Bearer está marcado ali, mais está vazio acho que pode ser isso, se eu não marco o Bearer ele cai na exception de authorizationHeader == null

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

solução!

Como essa é a requisição para efetuar login e obter o token, na aba bearer você não deve enviar nada. Confere se está assim (desmarcado):

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

Outra coisa, esse seu método está incorreto:

private String retrieveToken(HttpServletRequest request) {
    var authorizationHeader = request.getHeader("Authorization");

    if(authorizationHeader != null){
        return authorizationHeader.replace("Bearer ","");
    }
    return "Token não recuperado";
}

O último return deve devolver null:

private String retrieveToken(HttpServletRequest request) {
    var authorizationHeader = request.getHeader("Authorization");

    if(authorizationHeader != null){
        return authorizationHeader.replace("Bearer ","");
    }
    return null;
}

Opa obrigado deu certo, mais agora uma dúvida pq o retrono é null em recuperarToken ?

Pq tem esse código:

var tokenJWT = retrieveToken(request);
if(tokenJWT != null){
    var subject = tokenService.getSubject(tokenJWT);
    var usuario = repositorioUsuario.findByUsername(subject);
    SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(usuario, null, usuario.getAuthorities()));
}

Se não for null, vai entrar no if e tentar recuperar o subject. Era o que estava acontecendo no seu código, você não retornava null, então sempre entrava no if.

ah sim verdade, não me atentei a esse detalhe, e existe alguma forma de fazer a validação da senha ? tipo se alguém digitar uma senha inválida retornar a mensagem "usuário ou senha inválido" ? eu consegui fazer a validação do username mais queria ver se tem como do password.

Aqui tem um exemplo que trata mais erros, incluindo o caso de usuario/senha invalidos: https://cursos.alura.com.br/course/spring-boot-aplique-boas-praticas-proteja-api-rest/task/125341

Muito obrigado mesmo, curti muito o curso estou acabando a parte 3, e já trabalho na área e gostaria de me aprofundar mais, qual o próximo passo agora ?

Legal. Você pode estudar então Microsserviços ou DevOps (Docker, CI/CD, AWS, Azure, etc)

Bons estuidos!