1. Neste post vou explicar um pouco sobre o funcionamento do
JWT, como ele segue a especificação RFC 7519 é necessário usar
alguma implementação e para este post vou usar apache oltu que é
uma implementação da apache. Existem outras diversas
implementações como a do Spring e etc.
| Introdução
JSON Web Token (JWT) é um padrão aberto baseado na ( RFC
7519 ) que define uma estrutura compacta para tráfego de dados
úteis e com segurança nas permissões entre partes interessadas,
estes dados são validados e são assinados digitalmente, podemos
assinar um JWT usando um secret com o algoritmo HMAC ou
usando par de chaves público-privadas usando RSA. Por ser
compacto ele e rápido e pode ser enviado pela através da URL,
por parâmetro ou dentro de um header HTTP. O token JWT
carrega dados que serão úteis para a aplicação. O JWT se encaixa
perfeitamente em dois cenários, autenticações ou na troca de
informação, o uso mais comum hoje e em autenticações e ainda
mais que hoje temos a grande popularidade das aplicações single
page o JWT e uma ótima solução, o fluxo mais comum e ao
realizar o login na aplicação receber no retorno o token JWT que
será armazenado no localstorage e a cada nova requisição feita ao
backend será passado o Authorization: Bearer <token> no
cabeçalho HTTP que foi armazenado anteriormente no
Localstorage , quando o backend receber o token fará a validação
do token conferindo sua autenticidade, em caso de válido o
recurso é liberado, caso não retorna erro HTTP 403. No decorrer
do post vou detalhar melhor o uso e estrutura do JWT.
2. | Estrutura
JSON Web Token contém 3 partes:
Header
Payload
Signature
O JWT geralmente fica da seguinte forma:
header.payload.signature
As partes são divididas por (.), a seguir vamos detalhar cada uma
das partes.
| Header
A primeira parte da estrutura do JWT é o header que contém as
informações sobre qual criptografia está sendo usada, podemos
ver um exemplo logo abaixo, o typ representa o tipo que e JWT e
o alg representa o algoritmo que está sendo usado, podemos usar
algoritmos HMAC SHA ou RSA e cty e o content-type.
Atributos do header:
typ (Tipo do token)
alg (Define o algoritmo usado)
cty (Define content-type)
{
"typ": "JWT",
"alg": "HS256",
"cty": "text/html"
}
3. | Tipos de algoritmos
Tipo Algoritmo Descrição
HS256 HMAC using SHA-256
HS384 HMAC using SHA-384
HS512 HMAC using SHA-512
RS256 RSASSA-PKCS1-v1_5 using SHA-256
RS384 RSASSA-PKCS1-v1_5 using SHA-384
RS512 RSASSA-PKCS1-v1_5 using SHA-512
ES256 ECDSA using P-256 and SHA-256
ES384 ECDSA using P-384 and SHA-384
ES512 ECDSA using P-521 and SHA-512
PS256 RSASSA-PSS using SHA-256 and MGF1 with SHA-256
PS384 RSASSA-PSS using SHA-384 and MGF1 with SHA-384
PS512 RSASSA-PSS using SHA-512 and MGF1 with SHA-512
| Payload (Claims)
Claims reservados
Essas são as claims definidas pela especificação IANA JSON
Web Token Claims estas claims já são pré-definidas, não são
obrigatórias (Mas recomendadas), são um conjunto de
informações úteis e interoperáveis podendo ser utilizados por
protocolos de segurança em várias APIs.
4. lista de claims pré-definidas abaixo:
Nome Descrição Claim
iss Issuer(Quem emitiu o token)
sub Subject(Quempertence o token, geralmente id do usuário)
aud Audience(identifica os destinatários para os quais o JWT se destina)
exp Expiration Time(Timestamp quando expira)
nbf Not Before(Timestamp a partir de quando o token pode ser usado)
iat Issued At(Timestamp quando foi emitido o token)
jti JWT ID (identificador único do token.)
Exemplo de payload:
{
"sub": "33743568",
"name": "Rodrigo araújo",
"admin": true,
....
}
5. Claims públicos
São atributos que definem o uso do JWT e informações úteis para
a aplicação o ideal é usar identificadores diferentes para evitar
colisões, uma vez que são claims públicas o ideal é fazer o uso de
URI ou URL para reduzir a possibilidade de colisões.
{
"http://www.site.com.br/admin": "true"
}
Claims privados
Assim como os claims públicos os privados também necessitam
de evitar colisões.Tanto os claims públicos quanto os privados
devem ser usados com moderação para manter o token o menor
possível usando apenas informações necessárias. Podemos usar
com id de usuário ou qualquer outro dado,sempre opte por nomes
curtos para manter os JWT compactos.
| Signature
A signature e composta por header e payload convertidos para
base64 e mais o secret. No exemplo abaixo a signature foi criado
usando SHA256
HMAC.
String secret = “txt_secreto”;
String header = base64(header);
String payload = base64(payload);
String signature = HMACSHA256(header+"."+payload , secret);
String token = header+"."+payload+"."+signature;
6. Print do token JWT:
Agora vamos fazer uma demonstração da criação e validação do
JWT e para isso vamos usar o apache Oltu que e uma
implementação do JWT (RFC 7519).
| Dependências Oltu
eyJhbGciOiJTSEEyNTYiLCJ0eXAiOiJKV1QifQ
.eyJhdWQiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20vby9vYXV0aDIvdG9rZW4
iLCJpc3MiOiI3ODg3MzIzNzIwNzgtcGFzNmM0dHF0dWRwb2NvMmY0YXUxOGUwM
HN1ZWRqdGJAZGV2ZWxvcGVyLmdzZXJ2aWNlYWNjb3VudC5jb20iLCJleHAiOjE0O
TUzMzYzNDQsImlhdCI6MTQ5NTMzMjc0NCwic2NvcGUiOiIgaHR0cHM6Ly93d3cuZ2
9vZ2xlYXBpcy5jb20vYXV0aC9wbHVzLmxvZ2luIn0
.9kt1zMrD5XK57KVNLQRy_3h2vpz7Pm7nVt7M4hgcfdE
<!--
https://mvnrepository.com/artifact/org.apache.oltu.oauth2/org.apache.oltu.oaut
h2.jwt -->
<dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.jwt</artifactId>
<version>1.0.0</version>
</dependency>
<!--
https://mvnrepository.com/artifact/org.apache.oltu.jose/org.apache.oltu.jose.jws
-->
<dependency>
<groupId>org.apache.oltu.jose</groupId>
<artifactId>org.apache.oltu.jose.jws</artifactId>
<version>1.0.0</version>
</dependency>
7. | Exemplo JWT
//chave que é usada na assinatura
String pk = "nomeSecreat";
// Classe que possui métodos de criação de signature e validação.
SignatureMethodsHMAC256Impl sHmacImpl = new
SignatureMethodsHMAC256Impl();
// classe que recebe o array de bytes do secret para gerar signature
SymmetricKeyImpl key = new SymmetricKeyImpl(pk.getBytes());
//Criar estrutura jwt
JWT jwt = new JWT.Builder()
.setHeaderType(TipoTyp.JWT.getValor())
. .setHeaderAlgorithm(TipoAlgoritimo.SHA256.getValor())
.setClaimsSetIssuer("https://api.site.com")
.setClaimsSetSubject("33743568")
.setClaimsSetAudience("https://accounts.site.com/o/oauth2/token")
.setClaimsSetExpirationTime(System.currentTimeMillis() / 1000 +3600)
.setClaimsSetIssuedAt(System.currentTimeMillis() / 1000)
.setClaimsSetJwdId("id_87594353")
.setClaimsSetCustomField("nome","Rodrigo Araújo")
.setClaimsSetCustomField("admin",true)
.build();
String header = new JWTHeaderWriter().write(jwt.getHeader());
String payload = new JWTClaimsSetWriter().write(jwt.getClaimsSet());
System.out.println(jwt.getHeader());
/* Imprime: {typ: JWT, alg: SHA256,cty:null} */
System.out.println(header);
/* O JWTHeaderWriter().write validar se tem algum atributo nulo */
/* Imprime: {alg:SHA256,typ:JWT} */
8. public static void main(String[] args) {
System.out.println(jwt.getClaimsSet());
/* Imprime: {iss: https://api.site.com,
sub: 33743568,
aud: https://accounts.google.com/o/oauth2/token,
exp: 1495383435,
nbf: null,
iat: 1495379835,
jti: id_87594353,
typ: null } */
System.out.println(payload);
/* O JWTClaimsSetWriter().write validar se tem algum atributo nulo */
/* Imprime: {aud:"https://accounts.google.com/o/oauth2/token",
iss:"https://api.site.com",
jti:"id_87594353",
sub:"33743568",
exp:1495383435,
iat:1495379835,
admin:true} */
//converte header e payload em base64
JWTReader jheader = new JWTReader();
header = jheader.base64Encode(header);
payload = jheader.base64Encode(payload);
//cria a signature usando SHA256
String signature = sHmacImpl.calculate(header, payload, key);
System.out.println(signature );
//Imprime a signature - qMwn57yT1g9azJF-zDatMliElq-rdFNgkuB1gTQwfLY
String token = header+"."+payload+"."+signature;
System.out.println(token);
/* Imprime o token JWT :
eyJhbGciOiJTSEEyNTYiLCJ0eXAiOiJKV1QifQ
.eyJhdWQiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20vby9vYXV0aDIvd
G9rZW4iLCJpc3MiOiJodHRwczovL2FwaS5zaXRlLmNvbSIsImp0aSI6ImlkXzg3
NTk0MzUzIiwic3ViIjoiMzM3NDM1NjgiLCJleHAiOjE0OTUzODQ1MzksImlhdCI6
MTQ5NTM4MDkzOSwiYWRtaW4iOnRydWV9
.qMwn57yT1g9azJF-zDatMliElq-rdFNgkuB1gTQwfLY
O token pode ser enviado para o browser...
*/
}
9. A imagem abaixo reflete muito bem o fluxo, na primeira ação a
aplicação faz login mandando um POST com usuário e senha, ao
se confirmar as credenciais enviadas será criado o token JWT que
é retornado para o browser armazená-lo e a cada nova requisição
que for realizada o token será passado no header Authorization:
Bearer<token>
O último fluxo vai checar a autenticidade do token JWT onde a
aplicação checar o token com a secret e fará a validação, caso seja
um token válido o recurso é liberado.
Boolean autentico = sHmacImpl.verify(signature, header, payload, key);
if(autentico){
System.out.println("Token válido");
}
else
{
System.out.println("Token inválido");
}
10. Este é um primeiro post sobre JWT mais fazendo uma breve
explanação, os próximos posts serão mais práticos e os próximos
serão:
1 - Usar a autenticação com JWT e angular; 1.x
2 - Autenticações com OAuth2 - Implementação do apache Oltu:
OAuth 2.0 Authorization Server
OAuth 2.0 Client
OAuth 2.0 Resource Server
OAuth 2 server to server
3 - Autenticações com OAuth2 e angular 1.x
Ate à próxima...
REFERÊNCIAS
https://tools.ietf.org/html/rfc7519
https://jwt.io
https://oltu.apache.org/