Explicação detalhada da autenticação de token baseada em JWT (implementação Java)
Dicas antes de visualizar:
A versão IDEA usada neste artigo é 2019.1 final, e a versão JDK é 1.8.0_141.
1. Introdução
Na autenticação de identidade de computador, significa token (temporário). O token é realmente mais popular e pode ser chamado de código secreto. Antes de alguma transmissão de dados, o código secreto deve ser verificado primeiro. Diferentes códigos secretos são autorizados para diferentes operações de dados.
2.JWT
JSON Web Token, JSON Web token, nosso exemplo a seguir também é baseado na autenticação de token implementada por JWT, o seguinte é uma string JWT completa
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODMyOTQxNzEsInVzZXJOYW1lIjoidXNlciIsInBhc3N3b3JkIjoiMTExMTExMTEifQ.t6JLktclylpt2s5T_B1KDxYCJALr-fbZsy1J_YT57Jk
É composto por três partes: cabeçalho, carga útil e assinatura
2.1 cabeçalho
Consiste em duas partes
-
O tipo de declaração é JWT.
-
Declare o algoritmo de criptografia, use HMAC SHA256
A parte do cabeçalho JWT é um objeto JSON, conforme mostrado abaixo
{
'typ': 'JWT',
'alg': 'HS256'
}
Criptografe-o com Base64 e o JWT constitui a primeira parte.
2.2 carga útil
A parte da carga útil é a parte principal do conteúdo do JWT e também é um objeto JSON, composto de três partes
-
Declaração registrada na norma
-
Declaração pública
-
Declaração privada
A declaração registrada no padrão fornece sete campos padrão.
-
iss: emissor
-
exp: tempo de expiração
-
sub: Assunto
-
aud: usuário
-
nbf: Não disponível antes
-
iat: hora de lançamento
-
jti: O ID do JWT é usado para identificar o JWT
Declaração pública
A declaração pública pode adicionar qualquer informação, geralmente adicionando informações relacionadas ao usuário ou outras informações necessárias exigidas pela empresa (informações confidenciais não são recomendadas).
Declaração privada
Uma declaração privada é uma declaração definida em conjunto pelo provedor e pelo consumidor (informações confidenciais não são recomendadas).
O exemplo a seguir é um exemplo de carga útil
{
"sub": "1234567890",
"name": "userName",
"admin": true
}
Criptografe-o com Base64 para formar a segunda parte do JWT.
Nota: JWT não é criptografado por padrão e qualquer pessoa pode interpretar seu conteúdo, portanto, não construa campos de informações privadas e armazene informações confidenciais para evitar vazamento de informações.
2.3 assinatura
A parte do hash da assinatura serve para assinar as duas partes dos dados acima e gerar um hash por meio do algoritmo especificado para garantir que os dados não sejam violados. Consiste em três partes:
-
cabeçalho (criptografado por Base64)
-
carga útil (criptografado em Base64)
-
segredo
O servidor precisa de uma chave secreta, que é armazenada apenas no servidor e não pode ser divulgada aos usuários. Em seguida, use o algoritmo de assinatura especificado no cabeçalho (HMAC SHA256 por padrão) para gerar uma assinatura de acordo com a seguinte fórmula.
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Depois que o hash de assinatura é calculado, as três partes do cabeçalho JWT, carga útil e hash de assinatura são combinadas em uma string e cada parte é separada por "." Para formar o objeto JWT inteiro.
3. Use
O cliente recebe o JWT retornado pelo servidor e o armazena em Cookie ou localStorage.
Depois disso, o cliente trará o JWT ao interagir com o servidor. Se estiver armazenado em um Cookie, pode ser enviado automaticamente, mas não cruzará domínios, portanto, geralmente é colocado no campo Autorização de cabeçalho da solicitação HTTP.
Use o formato a seguir
Authorization: Bearer JWT
Todo o processo de interação do JWT é mostrado na figura abaixo
4. Exemplos
Este exemplo fornece apenas Java para implementar a assinatura e verificação JWT, e o uso de transmissão http será adicionado posteriormente.
O pacote jar que pom.xml precisa fazer referência
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.10.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.10.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.2</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.10.2</version>
</dependency>
Classe de ferramenta TokenUtil.java
package token;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class TokenUtil {
//token过期时长30分钟
private static final long EXPIRE_TIME = 30 * 60 * 1000;
//token私钥
private static final String TOKEN_SECRET = "abcdefg";
public static String sign(String userName, String password) {
String signData = "";
//过期时间
Date date = new Date(System.currentTimeMillis() + EXPIRE_TIME);
//私钥及加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//设置头信息
Map<String, Object> header = new HashMap();
header.put("typ", "JWT");
header.put("alg", "HS256");
signData = JWT.create()
.withHeader(header)
.withClaim("userName", userName)
.withClaim("password", password)
.withExpiresAt(date)
.sign(algorithm);
return signData;
}
/**
* @Description token解码校验
* @param token
* @return
* @Create 2020-03-03 by jjy
*/
public static boolean verfiy(String token) {
try {
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
JWTVerifier jwtVerifier = JWT.require(algorithm).build();
DecodedJWT decodedJWT = jwtVerifier.verify(token);
String userName = decodedJWT.getClaim("userName").asString();
String password = decodedJWT.getClaim("password").asString();
if(!Test.USERNAME.equals(userName) || !Test.PASSWORD.equals(password)) {
return false;
}
if(new Date().getTime() > decodedJWT.getExpiresAt().getTime()){
return false;
}
} catch (Exception e) {
return false;
}
return true;
}
}
Classe de teste Test.java
package token;
public class Test {
public static final String USERNAME = "user";
public static final String PASSWORD = "11111111";
public static void main(String[] args) {
String token = TokenUtil.sign(USERNAME, PASSWORD);
System.out.println("加密后的token为:" + token);
boolean flag = TokenUtil.verfiy(token);
if(flag){
System.out.println("校验成功");
} else {
System.out.println("校验失败");
}
}
}
Os resultados são os seguintes