JWT详解笔记

🔑JWT(JSON Web Token)

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络应用之间安全地传输信息。它是一种轻量级的认证和授权机制,常用于身份验证和信息传递。

JWT由三部分组成,通过点号(.)分隔开来:

  1. Header(头部):包含关于令牌的元数据和算法的信息。例如,指定令牌使用的签名算法(例如HMAC、RSA等)和令牌类型(如JWT)。

  2. Payload(负载):包含称为声明(claims)的信息,它是JWT的主要内容。声明是关于实体(通常是用户)和其他数据的陈述。有三种类型的声明:

    • Registered claims(注册声明):这些是预定义的声明,包括iss(令牌签发者)、exp(过期时间)、sub(主题)等。
    • Public claims(公共声明):这些是自定义的声明,根据应用程序的需要定义。
  • Private claims(私有声明):这些是自定义的声明,用于在同意的各方之间共享信息,但不是注册的或公共的声明。
  1. Signature(签名):使用头部和负载经过签名算法计算得出的签名,用于验证令牌的完整性和真实性。签名使用一个密钥(对称加密)或一对公私钥(非对称加密)进行生成和验证。

     

JWT的工作流程如下:

  1. 用户通过提供其凭据(如用户名和密码)进行身份验证。
  2. 服务器验证凭据,并在验证成功后生成JWT。
  3. 服务器将JWT发送回客户端。
  4. 客户端将JWT存储在本地(通常是在浏览器的本地存储或Cookie中)。
  5. 客户端在每个后续请求中将JWT包含在请求的头部、查询参数或Cookie中。
  6. 服务器验证JWT的签名和有效期,并根据其中的声明对请求进行授权和身份验证。

由于JWT是自包含的,它不需要在服务器端存储会话信息,因此具有良好的可扩展性。它也可以在分布式系统中使用,允许不同服务之间共享用户身份和授权信息。


 

Structure

A JWT contains three parts:

  • 头部 Header

    : Consists of two parts:

    • The signing algorithm that’s being used.
    • The type of token, which, in this case, is mostly “JWT”.
  • 载荷 Payload: The payload contains the claims or the JSON object.

  • 签名 Signature: A string that is generated via a cryptographic algorithm that can be used to verify the integrity of the JSON payload.

image-20240216203256224

 

How do they work

The easiest way to explain how a JWT works is via an example. We will start by creating a JWT for a specific JSON payload and then go about verifying it:

1) Create a JSON

Let’s take the following minimal JSON payload:

1
2
3
4
{
"userId": "abcd123",
"expiry": 1646635611301
}

 

2) Create a JWT signing key and decide the signing algorithm

First, we need a signing key and an algorithm to use. We can generate a signing key using any secure random source. For the purpose of this post, let’s use:

  • Signing key: NTNv7j0TuYARvmNMmWXo6fKvM4o6nv/aUi9ryX38ZH+L1bkrnD1ObOQ8JAUmHCBq7Iy7otZcyAagBLHVKvvYaIpmMuxmARQ97jUVG16Jkpkp1wXOPsrF9zwew6TpczyHkHgX5EuLg2MeBuiT/qJACs1J0apruOOJCg/gOtkjB4c=
  • Signing algorithm: HMAC + SHA256, also known as HS256.

 

3) Creating the “Header”

This contains the information about which signing algorithm is used. Like the payload, this is also a JSON and will be appended to the start of the JWT (hence the name header):

1
2
3
4
{
"typ": "JWT",
"alg": "HS256"
}

 

4) Create a signature
  • First, we remove all the spaces from the payload JSON and then base64 encode it to give us eyJ1c2VySWQiOiJhYmNkMTIzIiwiZXhwaXJ5IjoxNjQ2NjM1NjExMzAxfQ. You can try pasting this string in an online base64 decoder to retrieve our JSON.

  • Similarly, we remove the spaces from the header JSON and base64 encode it to give us: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.

  • We concatenate both the base 64 strings, with a . in the middle like <header>.<payload>, giving us eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJhYmNkMTIzIiwiZXhwaXJ5IjoxNjQ2NjM1NjExMzAxfQ. There is no special reason to do it this way other than to set a convention that the industry can follow.

  • Now we run the Base64 + HMACSHA256 function on the above concatenated string and the secret to give us the signature:

    1
    2
    3
    4
    5
    6
    Base64URLSafe(
    HMACSHA256("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJhYmNkMTIzIiwiZXhwaXJ5IjoxNjQ2NjM1NjExMzAxfQ", "NTNv7j0TuYARvmNMmWXo6fKvM4o6nv/aUi9ryX38ZH+L1bkrnD1ObOQ8JAUmHCBq7Iy7otZcyAagBLHVKvvYaIpmMuxmARQ97jUVG16Jkpkp1wXOPsrF9zwew6TpczyHkHgX5EuLg2MeBuiT/qJACs1J0apruOOJCg/gOtkjB4c=")
    )

    Results in:
    3Thp81rDFrKXr3WrY1MyMnNK8kKoZBX9lg-JwFznR-M

    We base64 encode it only as an industry convention.

 

5) Creating the JWT

Finally, we append the generated signature like <header>.<body>.<signature> to create our JWT:

1
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiJhYmNkMTIzIiwiZXhwaXJ5IjoxNjQ2NjM1NjExMzAxfQ.3Thp81rDFrKXr3WrY1MyMnNK8kKoZBX9lg-JwFznR-M

 

6) Verifying the JWT

The auth server will send the JWT back to the client’s frontend. The frontend will attach the JWT to network requests to the client’s api layer. The api layer will do the following steps to verify the JWT:

  • Fetches the header part of the JWT (eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9).
  • Does base64 decoding on it to get the plain text JSON: {"typ":"JWT","alg":"HS256"}
  • Verifies that the typ field’s value is JWT and the alg is HS256. If not, it would reject the JWT.
  • Fetches signing secret key and runs the same Base64URLSafe(HMACSHA256(...)) operation as step number (4) on the header and body of the incoming JWT. Note that if the incoming JWT’s body is different, this step will generate a different signature than in step (4).
  • Checks that the generated signature is the same as the signature from the incoming JWT. If it’s not, then the JWT is rejected.
  • We base64 decode the body of the JWT (eyJ1c2VySWQiOiJhYmNkMTIzIiwiZXhwaXJ5IjoxNjQ2NjM1NjExMzAxfQ) to give us {"userId":"abcd123","expiry":1646635611301}.
  • We reject the JWT if the current time (in milliseconds) is greater than the JSON’s expiry time (since the JWT is expired).