(JWT) JSON Web Token
Conhecendo o (JWT) na teoria e na prática.
Introdução
Quando estamos trabalhando com API’s, nós precisamos pensar na segurança no momento no momento de trafegar os dados e principalmente no nível de permissão que cada usuário deverá ter. Existem muitas formas de se fazer isso, mas uma que vem se destacando a cada dia que se passa é o JWT (JSON Web Token), por ser muito seguro e fácil de se implementar. Nas próximas sessões, será abordado a sua teoria com alguns exemplos e no final iremos criar uma aplicação com Node.js para exemplificarmos melhor o seu funcionamento.
Bom, o JWT (JSON Web Token) é um sistema de transferência de dados que pode ser enviado via POST ou em um cabeçalho HTTP (header) de maneira “segura”, essa informação é assinada digitalmente por um algoritmo HMAC, ou um par de chaves pública/privada usando RSA. Podemos ver na imagem a baixo um cenário onde será requisitado um token através do Verbo HTTP POST, que irá devolver um token validado para que nas próximas requisições que utilizem os Verbos HTTP possam utilizar.
A estrutura dele é formada por 3 partes: Header, Payload e Signature. Vamos entender melhor elas:
Header
O Header consiste em duas partes encodados em Base64 sendo:
- O Tipo (JWT)
- E o algoritmo de Hash que pode ser (HMAC SHA256 ou RSA).
{
"alg": "HS256",
"typ": "JWT"
}
Payload (Claims)
Os payloads são objetos JSON que contem os claims, nessa parte que nós trabalhamos com os “pedidos”, carga de dados ou dados enviados. Existem 3 tipos de claims em Payloads: reserved, public, e private claims.
Reserved claims: Atributos não obrigatórios (mas recomendados) que podem ser um conjunto de informações uteis e interoperáveis normalmente utilizados por protocolos de segurança em API’s:
- “iss” (Issuer) Origem do token
- “iat” (issueAt) Timestamp de quando o token foi gerado
- “exp” (Expiration) Timestamp de quando o token expira
- “sub” (Subject) Entidade a quem o token pertence, normalmente o ID do usuário
- Public claims: São atributos que definem o uso do JWT e informações úteis para a aplicação.
- Private claims: São atributos definidos especialmente para compartilhar informações entre aplicações.
Exemplo:
{
"iss": "127.0.0.13",
"exp": 1300819380,
"user": "programadriano",
"admin": true
}
Signature
Essa é a terceira e última parte do JWT, para que possamos ter um token, nós precisamos de um Header, Payload, o algoritmo definido na header, um secret definido pela nossa aplicação. Vamos ver um exemplo com a utilização do HMAC SHA256:
var encodedString = base64UrlEncode(header) + "." + base64UrlEncode(payload); + "." +
HMACSHA256(encodedString, 'secret');
O seu retorno seria o token a baixo:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1bmlxdWVfbmFtZSI6IlRoaWFnbyIsInN1YiI6IjEzIiwianRpIjoiZDBlMGFkZDItOTlkMC00NWY1LThlYzEtY2FiYzIwZjkxMGYyIiwiaWF0IjoxNTAwMDMzMjE0LCJKd3RWYWxpZGF0aW9uIjoiVXN1YXJpbyIsIm5iZiI6MTUwMDAzMzIxMywiZXhwIjoxNTAwMDMzMjczLCJpc3MiOiJJc3N1ZXIiLCJhdWQiOiJBdWRpZW5jZSJ9.SmjuyXgloA2RUhIlAEetrQwfC0EhBmhu-xOMzyY3Y_Q
Podemos perceber que o nosso token está dividido em três partes separadas por “.”, conforme nós utilizamos no momento da criação.
Primeira parte: Header .
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
Segunda parte: Payload.
eyJ1bmlxdWVfbmFtZSI6IlRoaWFnbyIsInN1YiI6IjEzIiwianRpIjoiZDBlMGFkZDItOTlkMC00NWY1LThlYzEtY2FiYzIwZjkxMGYyIiwiaWF0IjoxNTAwMDMzMjE0LCJKd3RWYWxpZGF0aW9uIjoiVXN1YXJpbyIsIm5iZiI6MTUwMDAzMzIxMywiZXhwIjoxNTAwMDMzMjczLCJpc3MiOiJJc3N1ZXIiLCJhdWQiOiJBdWRpZW5jZSJ9.
Terceira parte: Secret.
SmjuyXgloA2RUhIlAEetrQwfC0EhBmhu-xOMzyY3Y_Q
Quando um usuário faz uma requisição ao servidor passando os seus dados de login, o servidor analisa e retorna um token com uma validade, para que o usuário possa navegar pelo sistema.
Para que possamos validar se o nosso token está correto podemos utilizar o próprio site do JWT. Para isso, basta copiar o token acima que criamos para esse exemplo e colar na parte Encoded do site. Podemos ver o seu retorno dessa validação na imagem a baixo:
Agora lembra que acima comentamos sobre a palavra chave? Podemos adicionar ela na parte VERIFY SIGNATURE do site, para esse exemplo nós utilizamos a frase: batman batman batman, notem que após colocarmos ela irá mudar o status da nossa assinatura conforme a baixo de Invalid Signature para Signature Verified.
Nessa primeira parte nós conseguimos criar o nosso token e validar ele pelo próprio site do JWT, mas como funcionária em um projeto real? Vamos criar uma API em Node.js e utilizar o postman para que possamos validar as nossas requisições.
Exemplo prático
Nosso primeiro passo será a criação de um novo projeto Node.js, para que esse artigo não fique muito longo, vamos resumir essa etapa em:
1º Crie um diretório para o nosso projeto;
2º Execute o comando npm init -y;
3º Abrir uma IDE de nossa preferencia e criar uma estrutura básica como na imagem a baixo, para esse exemplo nós iremos utilizar o Visual Studio Code, ele é uma ferramente muito completa e grátis desenvolvida pela Microsoft. Caso queria instalar ele, basta baixar o seu executável no link Download.
Vamos agora adicionar os pacotes necessários para esse artigo, no seu arquivo package.json adicione as seguintes dependências:
},"dependencies": {"body-parser": "^1.15.0","express": "^4.13.4","jsonwebtoken": "^5.7.0","mongoose": "^4.4.5","morgan": "^1.7.0"}
Para que possamos baixar esse pacotes para o nosso projeto, execute o comando npm install, a execução dele irá criar uma pasta chamada node_modules e dentro dela os nossos pacotes.
Nosso próximo passo será a implementação da nossa classe user. Para isso, copie e cole o código a baixo dentro do seu arquivo user.js, que está dentro do diretório models.
class User {
constructor(name, admin) {
this.name = name;
this.admin = admin;
}};
module.exports = User;
Agora nós iremos implementar o nosso server.js, esse será o arquivo principal do nosso projeto.
var express = require('express');
var app = express();var bodyParser = require('body-parser');
var morgan = require('morgan');
var jwt = require('jsonwebtoken');
var user = require('./models/user');
Nessa primeira parte estamos importando os nossos pacotes, até aqui nenhuma novidade, vamos agora adicionar o código que será responsável para realizar a conversão dos nossos dados para JSON:
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
Vamos agora referenciar o código que será responsável por gerenciar os logs das nossas requisições e apresentar eles na nossa console:
app.use(morgan(‘dev’));
Para que possamos testar o pacote Morgan e validar o nosso código, vamos criar a nossa primeira rota:
var apiRoutes = express.Router();apiRoutes.get('/', function (req, res) {res.json({ message: 'Node.js com JWT' });});app.use('/', apiRoutes);var port = process.env.PORT || 8000;app.listen(port);console.log('Aplicação rodando na porta:' + port);
Vamos entender o código acima, na primeira linha nós estamos chamando o Router do pacote Express, em seguida nós criamos o nosso primeiro método Get retornando uma mensagem, por fim estamos passando a porta que o nosso projeto irá rodar e adicionando um listen nela. Para executar o código, dentro da console digite o comando node server.js, em seguida vamos abrir no nosso navegador o endereço: http://localhost:8000/ ele deve retornar a mensagem: “Node.js com JWT” e o senguinte log na nossa console:
Com isso nos conseguimos validar o pacote morgan e a nossa requisição HTTP com o express, vamos agora implementar o JWT. Para isso, vamos criar um novo método POST no nosso arquivo server.js.
apiRoutes.post('/', function (req, res) {if (req.body.UserName != "tadriano" || req.body.PassWord != "102030") {
res.json({ success: false, message: 'Usuário ou senha incorreto(s)!' });} else {let usuario = new user()
{
name : "tadriano";
admin: true
};var token = jwt.sign(usuario, 'batman batman batman', {
expiresInMinutes: 1440
});
res.json({
success: true,
message: 'Token criado!!!',
toke: token
});
}});
Para que esse artigo não fique muito longo, nós criamos uma validação conhecida como Hard Code, em seguida como não estamos buscando o nosso usuário no banco nós criamos um novo, para que possamos implementar os dados da nossa assinatura, em seguida nós retornamos uma mensagem de sucesso e o nosso token. Para validar essa etapa nós iremos utilizar o Postman, uma ferramenta do google chrome que auxilia nos testes a requisições HTTP. Com ele aberto adicione a URL do projeto com a porta e o usuário e senha na Body. A imagem a baixo demonstra esse implementação:
Feito isso podemos enviar a nossa requisição que irá retornar o nosso token. Podemos ver esse retorno na imagem a baixo:
Agora para finalizar esse artigo vamos criar um middler para verificar se a requisição está com o token ou se ele é um token é valido. Para isso, vamos colar o código a baixo depois do nosso método POST.
apiRoutes.use(function(req, res, next) {var token = req.body.token || req.query.token || req.headers['x-access-token'];if(token) {
jwt.verify(token, 'batman batman batman', function(err, decoded) {
if (err) {
return res.json({ success: false, message: 'Falha ao tentar autenticar o token!' });
} else {
//se tudo correr bem, salver a requisição para o uso em outras rotas
req.decoded = decoded;
next();
}
});} else {
// se não tiver o token, retornar o erro 403
return res.status(403).send({
success: false,
message: '403 - Forbidden'
});
}
});
Nessa etapa nós estamos verificando se a requisição está com o token, caso não esteja enviando ele ou caso ele seja um token invalido nós retornamos o status 403, que é utilizado para passar que o usuário não tem permissão para realizar aquela requisição. Caso o token seja valido, nós retornamos o status 200 e uma mensagem no navegador.
Por fim, para que possamos validar um requisição com o token, nós precisamos realizar o mesmo procedimento acima para gerar um token e em seguida preencher o Postman conforme a imagem a baixo, assim conseguiremos ter acesso aos nossos métodos.
Com isso nós conseguimos abordar a teoria do JWT e entender o seu funcionamento através de um exemplo criado em uma linguagem de programação que vem sendo adotada por muitos programadores a cada dia que se passa.