Para iniciar, adicione as dependências necessárias no arquivo pom.xml do projeto:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.12.6</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.12.6</version>
<scope>runtime</scope>
</dependency>
Classe Utilitária para Manipulação do Token
A seguir, a implementação da classe responsável pela criação e validação dos tokens JWT:
public final class TokenJwtHelper {
private TokenJwtHelper() {}
private static SecretKey obterChaveAssinatura(ConfiguracaoJwt props) {
return Keys.hmacShaKeyFor(
Decoders.BASE64URL.decode(props.getChaveSecreta())
);
}
public static String criarToken(Usuario usuario, ConfiguracaoJwt props) {
Instant agora = Instant.now();
Instant expiracao = agora.plusMillis(props.getTempoExpiracao());
return Jwts.builder()
.subject(usuario.getNomeUsuario())
.issuedAt(Date.from(agora))
.claim("identificador", usuario.getIdentificador())
.expiration(Date.from(expiracao))
.signWith(obterChaveAssinatura(props), Jwts.SIG.HS256)
.compact();
}
public static Claims extrairDados(String token, ConfiguracaoJwt props) {
return Jwts.parser()
.verifyWith(obterChaveAssinatura(props))
.build()
.parseSignedClaims(token)
.getPayload();
}
}
Preparação dos Componentes de Suporte
Propreidades de Configuração
Arquivo application.yml com as configurações do token:
seguranca:
jwt:
chave-secreta: QW5vdGhlclN0cmluZ1NlY3JldGFLZXlGb3JKd3RTaWduaW5nUHVycG9zZXMxMjM0NTY3OA==
tempo-expiracao: 7200000
nome-cabecalho: Authorization
Classe de Mapeamento das Propriedades
@Component
@ConfigurationProperties(prefix = "seguranca.jwt")
@Data
public class ConfiguracaoJwt {
private String chaveSecreta;
private long tempoExpiracao;
private String nomeCabecalho;
}
Estrutura de Resposta Padronizada
@Data
public class RespostaApi<T> implements Serializable {
private Integer codigo;
private String mensagem;
private T conteudo;
public static <T> RespostaApi<T> sucesso(T dados) {
RespostaApi<T> resposta = new RespostaApi<>();
resposta.setCodigo(200);
resposta.setConteudo(dados);
return resposta;
}
public static <T> RespostaApi<T> erro(String descricao) {
RespostaApi<T> resposta = new RespostaApi<>();
resposta.setCodigo(400);
resposta.setMensagem(descricao);
return resposta;
}
}
Constantes da Aplicação
public final class ConstantesJwt {
private ConstantesJwt() {}
public static final String CAMPO_IDENTIFICADOR = "identificador";
}
Implementação do Endpoint de Autenticação
Camada de Controle
@RestController
@RequestMapping("/auth")
public class AutenticacaoController {
@Autowired
private AutenticacaoService autenticacaoService;
@Autowired
private ConfiguracaoJwt configuracaoJwt;
@PostMapping("/entrar")
public RespostaApi<UsuarioResposta> autenticar(@RequestBody CredenciaisDTO credenciais) {
Usuario usuarioAutenticado = autenticacaoService.verificarCredenciais(credenciais);
String jwt = TokenJwtHelper.criarToken(usuarioAutenticado, configuracaoJwt);
UsuarioResposta dadosResposta = UsuarioResposta.builder()
.identificador(usuarioAutenticado.getIdentificador())
.nomeExibicao(usuarioAutenticado.getNomeUsuario())
.tokenAcesso(jwt)
.build();
return RespostaApi.sucesso(dadosResposta);
}
}
Camada de Serviço
public interface AutenticacaoService {
Usuario verificarCredenciais(CredenciaisDTO credenciais);
}
@Service
public class AutenticacaoServiceImpl implements AutenticacaoService {
@Autowired
private RepositorioUsuario repositorioUsuario;
@Override
public Usuario verificarCredenciais(CredenciaisDTO credenciais) {
Usuario cadastrado = repositorioUsuario.buscarPorNomeUsuario(credenciais.getNomeUsuario());
if (cadastrado == null) {
throw new RuntimeException("Credenciais inválidas");
}
if (!cadastrado.getSenha().equals(credenciais.getSenha())) {
throw new RuntimeException("Credenciais inválidas");
}
return cadastrado;
}
}
Camada de Acesso a Dados
@Mapper
public interface RepositorioUsuario {
@Select("SELECT * FROM usuarios WHERE nome_usuario = #{nomeUsuario}")
Usuario buscarPorNomeUsuario(String nomeUsuario);
}
Teste da Implementação
Requisição para o endpoint POST /auth/entrar:
POST http://localhost:8080/auth/entrar
Content-Type: application/json
{
"nomeUsuario": "maria_silva",
"senha": "senhaSegura123"
}
Exemplo de resposta obtida:
{
"codigo": 200,
"mensagem": null,
"conteudo": {
"identificador": 42,
"nomeExibicao": "maria_silva",
"tokenAcesso": "eyJhbGciOiJIUzI1NiJ9.eyJpZGVudGlmaWNhZG9yIjo0MiwiZXhwIjoxNzQxMTAzMzI2fQ.signatureAqui"
}
}