Explicación detallada de la autenticación de token basada en JWT (implementación de Java)
Consejos antes de ver:
La versión de IDEA utilizada en este artículo es la última 2019.1, y la versión JDK es 1.8.0_141.
1. Introducción
En la autenticación de identidad por computadora, significa token (temporal). El token es en realidad más popular y se puede llamar código secreto. Antes de alguna transmisión de datos, el código secreto debe verificarse primero. Se autorizan diferentes códigos secretos para diferentes operaciones de datos.
2.JWT
JSON Web Token, JSON Web token, nuestro siguiente ejemplo también se basa en la autenticación de token implementada por JWT, la siguiente es una cadena JWT completa
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODMyOTQxNzEsInVzZXJOYW1lIjoidXNlciIsInBhc3N3b3JkIjoiMTExMTExMTEifQ.t6JLktclylpt2s5T_B1KDxYCJALr-fbZsy1J_YT57Jk
Se compone de tres partes: encabezado, carga útil y firma
2.1 encabezado
Consta de dos partes
-
El tipo de declaración es JWT.
-
Declare el algoritmo de cifrado, use HMAC SHA256
La parte del encabezado JWT es un objeto JSON, como se muestra a continuación
{
'typ': 'JWT',
'alg': 'HS256'
}
Cifrelo con Base64, luego JWT constituye la primera parte.
2.2 carga útil
La parte de carga útil es la parte de contenido principal del JWT y también es un objeto JSON, compuesto por tres partes
-
Declaración registrada en el estándar
-
Declaración pública
-
Declaración privada
La declaración registrada en el estándar proporciona siete campos predeterminados.
-
iss: emisor
-
exp: tiempo de vencimiento
-
sub: Asunto
-
aud: usuario
-
nbf: No disponible antes
-
iat: tiempo de liberación
-
jti: el ID de JWT se utiliza para identificar el JWT
Declaración pública
La declaración pública puede agregar cualquier información, generalmente agregando información relacionada con el usuario u otra información necesaria requerida por la empresa (no se recomienda información confidencial).
Declaración privada
Una declaración privada es una declaración definida conjuntamente por el proveedor y el consumidor (no se recomienda información sensible).
El siguiente ejemplo es una carga útil de muestra
{
"sub": "1234567890",
"name": "userName",
"admin": true
}
Cifrelo con Base64 para formar la segunda parte de JWT.
Nota: JWT no está encriptado por defecto y cualquiera puede interpretar su contenido, por lo tanto, no construya campos de información privada y almacene información confidencial para evitar la filtración de información.
2.3 firma
La parte del hash de la firma es firmar las dos partes de datos anteriores y generar un hash a través del algoritmo especificado para garantizar que los datos no sean alterados. Consta de tres partes:
-
encabezado (cifrado por Base64)
-
payload (encriptado en Base64)
-
secreto
El servidor necesita una clave secreta, que solo se almacena en el servidor y no se puede revelar a los usuarios. Luego, use el algoritmo de firma especificado en el encabezado (HMAC SHA256 por defecto) para generar una firma de acuerdo con la siguiente fórmula.
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
Después de que se calcula el hash de la firma, las tres partes del encabezado JWT, la carga útil y el hash de la firma se combinan en una cadena, y cada parte está separada por "." Para formar el objeto JWT completo.
3. Utilice
El cliente recibe el JWT devuelto por el servidor y lo almacena en Cookie o localStorage.
Después de eso, el cliente traerá JWT cuando interactúe con el servidor. Si se almacena en una cookie, se puede enviar automáticamente, pero no cruzará dominios, por lo que generalmente se coloca en el campo Autorización de encabezado de la solicitud HTTP.
Utilice el formato de la siguiente manera
Authorization: Bearer JWT
Todo el proceso de interacción JWT se muestra en la siguiente figura
4. Ejemplos
Este ejemplo solo proporciona Java para implementar la firma y verificación JWT, y el uso de la transmisión http se agregará más adelante.
El paquete jar al que pom.xml necesita hacer referencia
<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>
Clase de herramienta 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;
}
}
Clase de prueba 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("校验失败");
}
}
}
Los resultados son los siguientes