Home 关于 JWT 的签名和验证
Post
Cancel

关于 JWT 的签名和验证

介绍 JWT(JSON Web Token)的文章有很多, 这里主要讲一下 JWT 的签名和验证,以及为什么签名是可信的。

由于 JWT 的签名和验证过程比较繁琐,一般情况下不需要自己手动实现。

我们可以通过调用已经封装好的库来轻松验证 JWT 是否有效,以及获取里面的信息。

这里是一些可用的库:JSON Web Token Libraries - jwt.io

JWT 签名和验证的过程

首先来看一下 JWT 的结构,下面是一个有效的 JWT:

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

这个长字符串被 2 个 . 分成 3 个部分 (segment), 我们把这三个部分分别叫做 头部(Header)、载荷(Payload) 和 签名(Signature)。

其中,Header 和 Payload 是通过 Base64 转码的,所以我们可以通过解码来获取实际的值:

Header

1
2
3
4
5
6
base64UrlDecode('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9')
=>
{
  "alg": "HS256",
  "typ": "JWT"
}

alg 表示签名哈希使用的算法, 常用的值有 RS256(RSA-SHA256)和 HS256(HMAC-SHA256)。 如果想看所有的算法可以看这篇文章:RFC 7518: JSON Web Algorithms (JWA)

Payload

1
2
3
4
5
6
7
base64UrlDecode('eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ')
=>
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

签名

签名(Signature) 是通过对 Base64 转码后的前两部分进行哈希(SHA256)后,再进行加密得到的结果:

注意签名时使用的哈希的算法必须要在 Header 的 alg 字段里说明, 这样接受方才能用对应的算法进行验证。本文的例子中用的是 HS256 算法

Signature

1
2
3
4
5
6
7
8
9
10
HMAC(
  SHA256(
    eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 +
    "." +
    eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
  )
  , 'your-256-bit-secret'
)
=>
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

关于 JWT 的生成,可以记住这个公式:

1
jwt = base64UrlEncode(header).base64UrlEncode(payload).HMAC(SHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload)), secret)

验证

由于 JWT 的 Payload 仅仅是通过 Base64 转码, 因此我们需要验证它的真实性,以保证它不是被人篡改过的。

首先我们需要对 Payload 进行 Base64 解码,并获取签名哈希的算法:

(与上面相同)

1
2
3
4
5
6
base64UrlDecode('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9')
=>
{
  "alg": "HS256",
  "typ": "JWT"
}

解码后,我们看到这个 JWT 使用了 HS256 算法进行签名。

所以我们需要使用相应的算法和密钥来验证签名。

在 HMAC 和 RSA 算法中签名和验证都需要使用密钥, 第三者在没有获得密钥的情况下无法伪造出正确的签名,所以这个验证方法是可信的。

RS256 和 HS256 的区别

两者的区别主要在于 RS256 是非对称算法(需要公钥和私钥), 而 HS256 则是对称算法(只有私钥)。 应该根据实际情况来选择合适的算法(官方则是推荐 RS256 算法)

RS256

由于私钥存放在本地,公钥存放在网络上并且可以随时被获取, 所以可以同时为多方提供服务(如谷歌等提供的 OpenID 服务)

在私钥泄露时,只需要更换本地的私钥以及网络上的公钥即可。

HS256

需要双方事先交换密钥,并且保证密钥的安全。 如果需要同时为多方提供服务时,会导致多方共用密钥,因此不安全。

密钥不慎泄露时需要双方同时更换密钥,因此比较麻烦。

参考资料

This post is licensed under CC BY 4.0 by the author.