Skip to main content

JWT 认证

JSON Web Token 是目前最流行的跨域认证解决方案。JWT 也就是我们常用的 token。

跨域问题

互联网无法离开用户认证,用户认证的流程一般如下:

  1. 用户输入用户名和密码,提交到服务器
  2. 服务器验证用户名和密码,如果正确,在当前对话(session)中记录用户的登录状态
  3. 服务器返回一个包含用户信息(session_id)的 cookie 给浏览器
  4. 浏览器将 cookie 保存起来,以后每次请求都会带上这个 cookie,将这个 seesion_id 发送给服务器
  5. 服务器验证 seesion_id,如果正确,就返回数据给浏览器

这种认证方式存在一个问题,就是扩展性不好。单个服务器自然没有问题,但是如果有多个服务器,就会出现问题。它要求 session 数据能够在服务器之间共享,这样才能保证用户在访问不同服务器的时候,能够保持登录状态。

举例,A 网站和 B网站是同一家公司的关联服务,现在要求用户只要在 A 网站登录了,就能在 B 网站自动登录。这就需要 session 数据能够在 A 网站和 B 网站之间共享。

一种解决方案是将 session 持久化,将 session 数据存储到数据库中,这样就能够在不同服务器之间共享了。但是这样会带来一个问题,就是服务器的负载会变重,因为每次请求都需要访问数据库。

另一种解决方案就是服务器不再保存 session 数据,将所有的数据都保存在客户端,每次请求都发回到服务器,服务器只需要验证数据的有效性即可。这种方式就是 JWT 认证。

JWT 原理

服务器认证后,生成一个 JSON 对象,发回给用户,如下:

{
"name": "Cheng",
"admin": true
"data": "2022-11-14",
}

此后,用户每次请求都会带上这个 JSON 对象,服务器只需要验证这个 JSON 对象的有效性即可。为了防止用户篡改数据,服务器会在生成 JSON 对象的时候,对其进行签名,如下:

{
"name": "Cheng",
"admin": true
"data": "2022-11-14",
"signature": "xxxxx"
}

实际的 JWT 大概像这样:

eydcjdnod9w7djwn82bwdkddko.
eyJkosdeLw0eo2hdbjJIu7RFCG08wd8d.
4psxcdfvekorw9d8f7g6h5j4k3l2m1n0

它是一个很长的字符串,由三部分组成,中间用点分隔开,分别是:HeaderPayloadSignature

2

Header 部分是一个 JSON 对象,用来描述 JWT 的元数据,如下:

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

alg 表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ 表示这个令牌(token)的类型(type),JWT 令牌统一写为 JWT。然后使用 Base64 编码,得到一个字符串。

Payload

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据,JWT 规定了7个官方字段,供选用,如下:

  • iss:JWT 的签发者
  • sub:JWT 所面向的用户
  • exp:JWT 的过期时间,这个过期时间必须要大于签发时间
  • aud:接收 JWT 的一方
  • nbf:定义在什么时间之前,该 JWT 都是不可用的(生效时间)
  • iat:JWT 的签发时间
  • jti:JWT 的唯一身份标识,主要用来作为一次性 token,从而回避重放攻击

除了官方字段,你还可以在这个部分定义私有字段,如下:

{
"name": "Cheng",
"admin": true
"data": "2022-11-14",
}

JWT 默认是不加密的,任何人都可以读到这个部分的数据,所以不要把敏感信息放在这个部分。

Signature

Signature 部分是对前两部分的签名,防止数据篡改。它由三部分组成,如下:

HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret
)

headerpayload 都是经过 Base64 编码的字符串,然后用点连接起来,再通过密钥 secret 进行 HMAC SHA256 运算,最后得到的就是 Signature。

前面提到,Header 和 Payload 串型化的算法是 Base64URL。这个算法跟 Base64 算法基本类似,但有一些小的不同。

JWT 作为一个令牌(token),有些场合可能会放到 URL(比如 api.example.com/?token=xxx)。Base64 有三个字符+、/和=,在 URL 里面有特殊含义,所以要被替换掉:=被省略、+替换成-,/替换成_ 。这就是 Base64URL 算法。

JWT 的使用

客户端收到 JWT,可以存储到 Cookie 或 Local Storage 中。

此后,客户端每次向服务器发送请求,都要带上这个 JWT。服务器收到请求,然后验证 JWT 的签名,如果通过,就知道这个请求是可信的。在 HTTP 请求中,JWT 通常被放在 Authorization 头信息中,如下:

Authorization: Bearer <token>

JWT 几个特点

  • JWT 默认是不加密的,任何人都可以读到里面的数据,所以不要把敏感信息放在 JWT 中。但是 JWT 是可以加密的。
  • JWT 的最大缺点是,由于服务器不保存 session 状态,因此在使用 JWT 时,每次用户请求都必须携带 JWT,这会轻微地降低性能。无法在使用过程中废止某个 token,或者更改 token 的权限。如果需要这些功能,就只能不使用 JWT,使用数据库来保存 session 状态。
  • JWT 本身包含认证信息,一旦泄露,任何人都可以获得该令牌的所有权限。为了减轻这种危险,JWT 的有效期应该设置得比较短。另外,一旦发现令牌泄露,应该立即废止该令牌。
  • JWT 不应该使用 HTTP 协议明码传输,要使用 HTTPS 协议传输。

各种语言的 JWT 实现