JWT | Domine JWT en un minuto | Conceptos y ejemplos

Autor: Salsa de Marte

Descargo de responsabilidad: este artículo fue escrito por Mars sauce y parte del contenido proviene de Internet. Si tiene alguna pregunta, comuníquese conmigo.

Reimpresión: ¡Bienvenido a la reimpresión, comuníquese conmigo antes de reimprimir!

¿Qué es JWT?

El nombre completo de JWT es Json Web Token. se basa en el estándar abierto RFC 7519, que define una forma compacta y autónoma de transmitir información de forma segura entre las partes en forma de objetos JSON. Esta información se puede utilizar para verificación y confianza mutua porque está firmada digitalmente. JWT se puede firmar con una clave privada (usando el algoritmo HMAC ) o con un par de claves pública/privada usando RSA o ECDSA .

¿Dónde se puede utilizar JWT?

Los siguientes son dos escenarios de uso de JWT:

  • 授权: Este es el caso de uso más común para usar JWT. Una vez que el usuario haya iniciado sesión, cada solicitud posterior contendrá el JWT, lo que permitirá al usuario acceder a las rutas, servicios y recursos permitidos con ese token. El inicio de sesión único es una característica en la que JWT se usa ampliamente en la actualidad debido a su bajo costo operativo y su facilidad de uso en diferentes dominios.
  • 信息交换: JWT es una forma relativamente conveniente de transmitir información de forma segura entre las partes. Debido a que los JWT se pueden firmar (por ejemplo, usando un par de claves pública/privada), es posible determinar si el remitente está autorizado por usted. Y, dado que la firma se calcula utilizando el encabezado y la carga útil, también es posible verificar que el contenido no haya sido manipulado.

Composición de JWT

Esta es una cadena de token JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Es muy complicado, ¿no lo entiendes? De hecho, esta cadena es la cadena de texto cifrado encriptada, que se .divide pasando por el medio. Cada .cadena anterior representa los tres componentes del JWT: Header, Payload, Signature.

Encabezado (información del encabezado)

La función principal de Header es identificar. Normalmente consta de dos partes:

  • typ: Abreviatura de tipo, tipo de token, es decir, JWT.
  • alg: Abreviatura de algoritmo, algoritmo de firma cifrada. Generalmente use HS256, el sitio web oficial de jwt proporciona 12 tipos de algoritmos de encriptación, la captura de pantalla es la siguiente:

Ejemplo de texto sin formato de encabezado:

{
  "alg": "HS256",
  "typ": "jwt"
}

El texto sin formato después de la codificación Base64 se convierte en:

eyJhbGciOiJIUzI1NiIsInR5cCI6Imp3dCJ9

也就是第一个.之前的密文串。以下是Header部分常用部分的声明:

key name 说明
typ 令牌类型 如果存在,则必须将其设置为已注册的 IANA 媒体类型。
cty 内容类型 如果使用嵌套签名或加密,建议将其设置为 ;否则,请省略此字段。
alg 消息身份验证代码算法 发行者可以自由设置算法来验证令牌上的签名。但是,某些受支持的算法不安全。
kid 密钥标识 指示客户端用于生成令牌签名的密钥的提示。服务器将此值与文件上的密钥匹配,以验证签名是否有效以及令牌是否真实。
x5c x.509 证书链 RFC4945 格式的证书链,对应于用于生成令牌签名的私钥。服务器将使用此信息来验证签名是否有效以及令牌是否真实。
x5u x.509 证书链网址 服务器可在其中检索与用于生成令牌签名的私钥对应的证书链的 URL。服务器将检索并使用此信息来验证签名是否真实。
crit 危急 服务器必须理解的标头列表,以便接受令牌为有效令牌

Payload(负载信息)

也称为JWT claims,放置需要传输的信息,有三类:

  • 保留claims:主要包括iss发行者、exp过期时间、sub主题、aud用户等。
  • 公共claims:定义新创的信息,比如用户信息和其他重要信息。
  • 私有claims:用于发布者和消费者都同意以私有的方式使用的信息。

以下是Payload的官方定义内容:

key name 说明
iss 发送者 标识颁发 JWT 的发送主体。
sub 主题 标识 JWT 的主题。
aud 接收者 标识 JWT 所针对的接收者。每个在处理 JWT 的主体都必须使用受众声明中的值来标识自己。如果处理的主体在存在此声明时未将自己标识为声明中的值,则必须拒绝 JWT。
exp 到期时间 标识不得接受 JWT 进行处理的过期时间。该值必须是日期类型,而且是1970-01-01 00:00:00Z 之后的日期秒。
nbf jwt的开始处理的时间 标识 JWT 开始接受处理的时间。该值必须是日期。
iat jwt发出的时间 标识 JWT 的发出的时间。该值必须是日期。
jti jwt id 令牌的区分大小写的唯一标识符,即使在不同的颁发者之间也是如此。

Payload明文示例:

{
  "sub": "12344321",
  "name": "Mars酱", // 私有claims
  "iat": 1516239022
}

经过Base64加密之后的明文,变为:

eyJzdWIiOiIxMjM0NDMyMSIsIm5hbWUiOiJNYXJz6YWxIiwiaWF0IjoxNTE2MjM5MDIyfQ

也就是第一个.和第二个. 之间的密文串内容。

Signatrue(签名信息)

Signature 部分是对Header和Payload两部分的签名,作用是防止 JWT 被篡改。这个部分的生成规则主要是是公式(伪代码)是:

Header中定义的签名算法(
    base64编码(header) + "." + base64编码(payload),
    secret
)

secret是存放在服务端加密使用到的盐。

得到签名之后,把Header的密文、Payload的密文、Signatrue的密文按顺序拼接成为一个字符串,中间通过.来连接并分割,整个串就是JWT了。

JWT实例

概念讲完了,我们来个实例吧,先来一个jwt的编码:

    public static String encodeJWT(String key) {
        // 1. 定义header部分内容 
        Map headerMap = new HashMap();
        headerMap.put("alg", SignatureAlgorithm.HS256.getValue());
        headerMap.put("typ", "JWT");

        // 2. 定义payload部分内容
        Map payloadMap = new HashMap();
        payloadMap.put("sub", "mars酱让你爽一分钟");
        payloadMap.put("iat", UUID.randomUUID());
        payloadMap.put("exp", System.currentTimeMillis() + 24 * 60 * 60 * 1000);
        payloadMap.put("name", "Mars酱");
        payloadMap.put("role", "酱油王");

        // 3.生成token
        String jwtToken = Jwts.builder()
                .setHeaderParams(headerMap)
                .setClaims(payloadMap)
                .signWith(SignatureAlgorithm.HS256, key)
                .compact(); // 拼接header + payload
//        System.out.println(jwtToken);
        return jwtToken;
    }

我们在main函数中调用这个,运行得到结果:

很长的密文字符串,就不截完整了。其中key是盐,jsonwebtoken的jar包规定,key必须字节数要大于等于你所用的加密算法的最小字节数,mars酱这里使用的是HS256,最小的key长度规定的就是256:

因此,key值我这里传入了一个超长的key:

String key = "marsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmarsmars";

如果你的key没这么长,你可能会报这样错误:

下面再来一个解密jwt的代码,入参为加密后的jwt和盐key:

    public static void decodeJWT(String jwtToken, String key) {
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey(key)
                    .parseClaimsJws(jwtToken)
                    .getBody();
            Object sub = claims.get("sub");
            Object name = claims.get("name");
            Object role = claims.get("role");
            Object exp = claims.get("exp");

            System.out.println("sub:" + sub + "\nname:" + name.toString() + "\nrole:" + role + "\nexp:" + exp + "\n失效了?" + ((System.currentTimeMillis() - (Long)exp) > 0));
        } catch (ExpiredJwtException e) {
            System.out.println("mars酱提醒您:token已过期");
        }
    }

在main中调用后,得到结果:

把生成的jwt字符串放入官网( jwt.io )的解密界面,和程序解密的结果一样,是不是很完美?

优缺点

好了,概念说完了,实例也给了,想想jwt有什么优缺点?我总结了一下:

优点:

  1. 可扩展性好 应用程序分布式部署的情况下,如果使用session机制,那就要要做多台机器的数据共享,通常可以存在数据库或者redis里面。而使用jwt不需要共享。
  2. jwt是无状态的 jwt不在服务端存储任何状态。RESTful API的原则之一是无状态,发出请求时,总会返回带有参数的响应,不会产生附加影响。用户的认证状态引入这种附加影响,这破坏了这一原则。另外jwt的载荷中可以存储一些常用信息,用于交换信息,有效地使用 JWT,可以降低服务器查询数据库的次数。

缺点:

  1. 安全性 由于jwt的payload是使用base64编码的,并没有加密,因此jwt中不能存储敏感数据。而session的信息是存在服务端的,相对来说更安全。
  2. 一次性 无状态是jwt的特点,但也导致了这个问题,jwt是一次性的。想修改里面的内容,就必须签发一个新的jwt。

jwt开源框架

mars酱这里使用的jwt是io.jsonwebtoken的,它需要在pom中引入依赖:

	<!-- jwt api定义 -->
	<dependency>
		<groupId>io.jsonwebtoken</groupId>
		<artifactId>jjwt-api</artifactId>
		<version>0.10.2</version>
	</dependency>
	<!-- jwt api impl实现 -->
	<dependency>
		<groupId>io.jsonwebtoken</groupId>
		<artifactId>jjwt-impl</artifactId>
		<version>0.10.2</version>
	</dependency>
	<!-- jwt json -->
	<dependency>
		<groupId>io.jsonwebtoken</groupId>
		<artifactId>jjwt-jackson</artifactId>
		<version>0.10.2</version>
	</dependency>

其余的jwt框架全在jwt官网列举了。基本覆盖了全语言

好好享受jwt的校验过程吧

Supongo que te gusta

Origin juejin.im/post/7232550589964140602
Recomendado
Clasificación