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)
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!