🔐 Cap 20: Login na Arena: Auth e Tokens
🎯 Objetivo da Aula: Ao final desta aula, você entenderá como os aplicativos mantêm você “logado” com segurança. Você aprenderá o que é um Token JWT e como enviá-lo para o servidor para provar que você é você mesmo antes de entrar em uma batalha online.
🏢 O Cenário Prático (Seu Desafio): Hackers estão tentando burlar o ranking do seu jogo enviando pontuações falsas! Eles fazem isso fingindo que são outros jogadores. Seu desafio é implementar a Trava de Segurança: o jogador deve fazer login com senha e receber um “Cartão de Acesso” (o Token). O servidor só aceitará comandos de quem mostrar esse cartão.
🧠 Fundamentos: A Teoria Traduzida
📖 Dicionário do Programador
- Autenticação: É o ato de provar quem você é (usando login e senha).
- Token JWT: É como um “Crachá Digital”. Ele contém seus dados criptografados e uma assinatura do servidor que ninguém consegue falsificar.
- Bearer Token: É o nome técnico do crachá. Significa: “Eu sou o portador (bearer) deste token, me deixe entrar”.
🎨 Padrão de Cabeçalho (Header)
Toda vez que você pede algo ao servidor (como ver seus itens), você coloca o token num “envelope” invisível chamado HTTP Header, na chave Authorization.
graph TD
A["App: Envia Senha"] --> B["Servidor: Valida"]
B --> C["Entrega Crachá: JWT"]
C --> D["App: Salva o Crachá"]
D --> E["Próxima Chamada: Mostra Crachá"]
🏗️ Construindo o Projeto (Checklist Studio)
No seu código que chama a API (Retrofit), você deve adicionar o cabeçalho:
Authorization: Bearer <SEU_TOKEN>
📦 Imports Necessários
Antes de copiar o código abaixo, adicione estes imports no topo do seu arquivo .kt:
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Response
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
💡
okhttp3.Interceptor,OkHttpClienteResponsejá vêm como dependência transitiva doretrofit(Cap 13) — não é necessário adicionar nada novo no Gradle.
📖 Exemplo Passo a Passo: Formatando o Crachá
Parte 1 — O Formato do Cabeçalho:
fun prepararCabecalho(token: String): String {
// Adicionamos a palavra mágica 'Bearer' antes do código
return "Bearer $token"
}
fun main() {
val meuToken = "abc123xyz"
println(prepararCabecalho(meuToken))
// Resultado: Bearer abc123xyz
}
Parte 2 — O Interceptor Real: em vez de adicionar o cabeçalho manualmente em cada chamada, criamos um Interceptor. Ele “intercepta” toda requisição do Retrofit e anexa o token automaticamente, antes de ela seguir para o servidor:
class AuthInterceptor(private val token: String) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val requisicaoOriginal = chain.request()
val requisicaoComToken = requisicaoOriginal.newBuilder()
.addHeader("Authorization", prepararCabecalho(token))
.build()
return chain.proceed(requisicaoComToken)
}
}
fun criarRetrofit(token: String): Retrofit {
val client = OkHttpClient.Builder()
.addInterceptor(AuthInterceptor(token))
.build()
return Retrofit.Builder()
.baseUrl("https://meu-jogo.beeceptor.com/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
💡 Repare que
criarRetrofité parecido com oRetrofit.Builder()do Cap 13, mas com uma linha extra:.client(client), que conecta oOkHttpClient(e seuAuthInterceptor) ao Retrofit.
🛠️ Prática Obrigatória 1:
Crie uma função chamada gerarHeader. Ela recebe uma String token e retorna a String formatada para o cabeçalho. Teste com o token "pokedex_gold_2026".
🛠️ Prática Obrigatória 2: Pesquise e anote: Por que é perigoso deixar o Token guardado para sempre no celular? O que é o “tempo de expiração” de um token?
🛠️ Prática Obrigatória 3:
Usando AuthInterceptor como referência, crie a classe LogInterceptor, que também implementa Interceptor. No método intercept, antes de chamar chain.proceed(...), ela deve imprimir println("📡 Chamando: ${chain.request().url}").
🔑 Gabarito Passo a Passo:
Exercício 2: O token expira para que, se alguém roubar o seu celular, o “crachá” pare de funcionar depois de algum tempo (ex: 1 hora), obrigando um novo login.
Exercício 3:
class LogInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val requisicao = chain.request()
println("📡 Chamando: ${requisicao.url}")
return chain.proceed(requisicao)
}
}
📤 Instruções de Entrega (Microsoft Teams):
- Envie o código da sua função de Header e da classe
AuthInterceptor. - Envie sua resposta sobre a expiração de tokens e o código do
LogInterceptor. - Submeta no canal de tarefas.