Na continuação da nossa série sobre autenticação no Spring Boot, vamos implementar o serviço responsável por gerar e validar tokens JWT. Essa etapa é fundamental para garantir que apenas usuários autenticados tenham acesso às funcionalidades protegidas da aplicação.
🎯 Objetivo
Neste artigo, vamos:
- Criar o serviço
JwtService
; - Entender como funcionam o secret key e o tempo de expiração do token;
- Implementar a geração e validação dos tokens JWT;
- Fazer as configurações necessárias no
application.properties
.
📦 Dependências do pom.xml
para JWT com Spring Boot
É essencial que o artigo contenha as dependências do pom.xml
para que o leitor consiga seguir o passo a passo sem erros, principalmente se estiver começando.
🛠️ Dependências necessárias no pom.xml
Para utilizar JWT com o Spring Boot, você precisa adicionar as bibliotecas abaixo ao seu pom.xml
:
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- JWT da biblioteca jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId> <!-- ou jjwt-gson -->
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
📝 Observação
Você pode adaptar a versão 0.11.5
conforme a versão do seu projeto, mas essa é a mais utilizada atualmente com o novo formato de dependência modular do jjwt
.
🛠️ Criação do JwtService
O JwtService
é a classe responsável por gerar, extrair e validar tokens. Ele usa a biblioteca jjwt, que é compatível com o Spring Boot e fácil de configurar.
@Service
public class JwtService {
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private long expiration;
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
public boolean isTokenValid(String token, String username) {
final String extractedUsername = extractUsername(token);
return extractedUsername.equals(username) && !isTokenExpired(token);
}
private boolean isTokenExpired(String token) {
return extractClaim(token, Claims::getExpiration).before(new Date());
}
private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
return claimsResolver.apply(claims);
}
private Key getSigningKey() {
return Keys.hmacShaKeyFor(secret.getBytes());
}
}
🔐 Explicando o uso do Secret e Expiration
No application.properties
, definimos as configurações de segurança:
# application.properties
jwt.secret=segredo-super-seguro-de-no-minimo-32-caracteres
jwt.expiration=86400000
jwt.secret
: É a chave usada para assinar os tokens. Ela deve ter no mínimo 256 bits para garantir segurança com o algoritmo HS256.jwt.expiration
: Tempo de expiração do token, em milissegundos. No exemplo, usamos 24 horas (86400000
ms).
Essas propriedades são injetadas na nossa classe com @Value
.
📥 Geração e validação do token
generateToken(String username)
: Cria um token com o nome do usuário como subject.extractUsername(String token)
: Lê o subject contido no token.isTokenValid(String token, String username)
: Verifica se o token pertence ao usuário informado e se ainda está válido.
Esses métodos são fundamentais para autenticar o usuário em requisições futuras.
🧠 Entendendo o JWT
Um JWT (JSON Web Token) é uma string codificada que representa uma identidade de forma segura. Ele possui três partes:
HEADER.PAYLOAD.SIGNATURE
Nós usaremos a biblioteca JJWT (Java JWT) da própria comunidade do Java para criar e verificar tokens.
🔍 Análise do Código
Vamos examinar a classe JwtService
passo a passo:
@Service
public class JwtService {
A anotação @Service
informa ao Spring que essa classe é um componente de serviço, permitindo sua injeção com @Autowired
.
🔑 Propriedades de configuração
@Value("${jwt.secret}")
private String secret;
@Value("${jwt.expiration}")
private long expiration;
Essas propriedades serão lidas do application.properties
. Por exemplo:
jwt.secret=chave-super-secreta-com-mais-de-32-caracteres
jwt.expiration=86400000 # 24 horas em milissegundos
secret
é a chave usada para assinar o token.expiration
define quanto tempo o token será válido (em milissegundos).
🔐 Gerando o token
public String generateToken(String username) {
return Jwts.builder()
.setSubject(username)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + expiration))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
Aqui estamos:
- Definindo o usuário que será o “dono” do token (
setSubject(username)
). - Informando a data de criação do token.
- Estabelecendo o prazo de validade.
- Assinando o token com nossa chave secreta.
- Retornando uma string compactada, que é o nosso JWT.
🧬 Extraindo o nome de usuário
public String extractUsername(String token) {
return extractClaim(token, Claims::getSubject);
}
Essa função extrai o “subject” do token — que, nesse caso, representa o nome de usuário. Ela faz isso chamando a função auxiliar extractClaim
.
🔍 O que é um “claim”?
Na estrutura de um token JWT, os claims são as informações que o token carrega. Eles são divididos em três tipos:
- Registered claims (padrão): como
sub
(subject),iss
(issuer),exp
(expiration). - Public claims: definidas publicamente e registradas na IANA.
- Private claims: criadas de forma personalizada por quem gera o token.
No nosso exemplo, usamos o claim "sub"
(subject), que é o nome de usuário, mas poderíamos extrair outros, como a data de expiração (exp
), o emissor (iss
) e até claims personalizados como role
, userId
, etc.
🔍 Extraindo qualquer dado (claim)
private <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
return claimsResolver.apply(claims);
}
Essa função genérica permite extrair qualquer informação (claim) do token. No caso do nome de usuário, usamos Claims::getSubject
.
❌ Verificando se o token expirou
private boolean isTokenExpired(String token) {
return extractClaim(token, Claims::getExpiration).before(new Date());
}
Essa função verifica se a data de expiração do token já passou.
✅ Verificando se o token é válido
public boolean isTokenValid(String token, String username) {
final String extractedUsername = extractUsername(token);
return extractedUsername.equals(username) && !isTokenExpired(token);
}
Um token JWT (JSON Web Token) é composto por três partes separadas por ponto (.
):
HEADER>.<PAYLOAD>.<SIGNATURE>
🧱 Exemplo real de token JWT (fictício, mas com estrutura real):
Você pode usar o site https://jwt.io/, para visualizar o conteúdo do token.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiIsImV4cCI6MTY5MjM0OTYwMH0.qmGp8m5s3E_2GdZwV6Z4CgLdspjC5Jre9GoR0mfL4NQ
🔍 Detalhando cada parte:
1. Header (Cabeçalho)
{
"alg": "HS256",
"typ": "JWT"
}
- Define o algoritmo de assinatura (ex: HS256)
- Tipo do token: JWT
2. Payload (Corpo com os claims)
{
"sub": "admin",
"exp": 1692349600
}
Aqui ficam os claims, ou seja, as informações declaradas dentro do token.
📌 Exemplos de claims comuns:
sub
: subject (usuário ou identificador único) →"admin"
exp
: expiration (tempo de expiração, em timestamp Unix)iat
: issued at (quando foi gerado)roles
: permissões do usuário (caso você deseje incluir)
Claim significa literalmente “declaração”. No JWT, é uma afirmação sobre um dado (como quem é o usuário ou quando o token expira).
3. Signature (Assinatura digital)
É gerada usando:
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)
Garante que ninguém alterou o conteúdo do token.
✅ Exemplo prático de um claim:
{
"sub": "kanechan",
"role": "ADMIN",
"exp": 1734567890
}
Neste caso:
sub
→ diz quem é o dono do token.role
→ diz que ele é um administrador.exp
→ define quando o token expira.
Aqui validamos se:
- O usuário extraído do token é igual ao usuário autenticado.
- O token ainda não expirou.
🔒 Gerando a chave de assinatura
private Key getSigningKey() {
return Keys.hmacShaKeyFor(secret.getBytes());
}
Essa função transforma a string secreta em uma chave do tipo Key
para ser usada nas assinaturas dos tokens.
🛠️ Estrutura de Pacotes
Essa classe está no pacote:
com.kanechan.restaurante.services
Como é um serviço de autenticação, o pacote services
é o local mais apropriado.
✅ Conclusão
Com essa etapa, finalizamos a construção do serviço de autenticação JWT. Agora nossa aplicação consegue gerar tokens seguros, verificar se estão válidos e extrair o usuário autenticado a partir deles.
No próximo artigo, vamos conectar isso tudo ao filtro JwtAuthenticationFilter
, que irá interceptar as requisições HTTP e garantir que apenas usuários autenticados tenham acesso.
🔜 Próximo artigo:
Como Proteger Endpoints com Filtro JWT no Spring Boot (Parte 3)