JWT | Master JWT in one minute | Concepts and examples

Author: Mars Sauce

Disclaimer: This article was written by Mars sauce, and part of the content comes from the Internet. If you have any questions, please contact me.

Reprint: Welcome to reprint, please contact me before reprinting!

What is JWT

The full name of JWT is Json Web Token. is based on the RFC 7519 open standard, which defines a compact and self-contained way to securely transmit information between parties in the form of JSON objects. This information can be used for verification and mutual trust because it is digitally signed. JWT can be signed with a private key (using the HMAC algorithm) or with a public/private key pair using RSA or ECDSA .

Where can JWT be used?

The following are two usage scenarios of JWT:

  • 授权: This is the most common use case for using JWT. Once the user is logged in, every subsequent request will contain the JWT, allowing the user to access the routes, services, and resources allowed with that token. Single sign-on is a feature where JWT is widely used today because of its low overhead and its ease of use across different domains.
  • 信息交换: JWT is a relatively convenient way to securely transmit information between parties. Because JWTs can be signed (for example, using a public/private key pair), it is possible to determine whether the sender is authorized by you. And, since the signature is calculated using the header and payload, it is also possible to verify that the content has not been tampered with.

Composition of JWT

This is a JWT token string:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

It's very complicated, don't you understand? In fact, this string is the encrypted ciphertext string, which is .divided by passing in the middle. Each .preceding string represents the three components of the JWT: Header, Payload, Signature.

Header (header information)

The main function of Header is to identify. Usually consists of two parts:

  • typ: Shorthand for type, token type, that is, JWT.
  • alg: Shorthand for Algorithm, encrypted signature algorithm. Generally use HS256, jwt official website provides 12 kinds of encryption algorithms, the screenshot is as follows:

Plaintext example of Header:

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

The plaintext after Base64 encoding becomes:

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的校验过程吧

Guess you like

Origin juejin.im/post/7232550589964140602
jwt