2 min read

JWT 机制

Table of Contents

前言

各种认证方式一直是我头痛的点。奇怪的是,几乎所有的 web 开发教程都默认你已经会了,而不会专门教学,只是一笔带过。你只能自己去找博客看。参考视频教程

jwt 归为一种身份验证(authentication)的方式。

令牌(token)

这个词挺神奇的。transformer 结构里有 token 这个概念,这里也有。不过他们的含义应该完全不同。在 web 开发中,token 指的是令牌,或者凭证。

最开始,人们使用 session+cookie 的认证方式。这种情景下,服务器存储用户的相关信息,仅返回给浏览器一个 session-id。浏览器下次请求,将在请求中的 cookie 中携带 session-id,服务器接收到这个 id,并且能查找到相关用户的信息,则认证成功。

但是这种模式越来越不适合当代多服务器的 web 模式。因为这相当于要求每个服务器都要存储一个用户信息表。所以,需要一种新的认证方式。

针对上述问题,一种解决方案是,不在服务器单独建用户信息表,而是将用户信息(token)存起来发给浏览器,浏览器下次来直接带完整的用户信息。然后,因为服务器这时候已经不存储任何用户信息,且所有信息完全来自用户,为了防止用户携带假信息进行欺诈,在发给浏览器的信息中加入签名进行防伪。

至于 JWT 在浏览器上的存储方式,可以是 cookie,也可以是 localstorage 等。

实现方式

jwt.io截图

上图是 jwt 的结构。三段式,分别是头部、负荷和签名。

  • 头部有两个字段:生成签名用到的加密算法 alg 和 token 类型 typ。默认的算法是HS256,这是一种对称加密算法,而那些以RS开头的算法,如RS256,则是非对称加密算法。

  • 负荷中的 iat 指的是 issue at,签发时间。还有一个常见字段是exp,表示过期时间。所有语言的jwt解析工具都会根据exp来判断是否过期。

  • 头部加负荷,再加上密钥,一同经过加密算法,生成了签名。头部和负荷都是由json经过base64编码生成的,所以等同于明文。验证操作时,验证方将自己生成的签名和jwt中的签名进行比较,如果相同,则验证通过。

jwt签名过程

注意secret有可能是base64编码过的。为什么需要对secret进行base64编码呢?

(1) 兼容不同的字符集。 JWT secret 如果包含特殊字符(如 @ # $ % ^ & *),在某些编程语言或数据库中可能会导致解析错误。Base64 只使用 字母、数字、+、/(Base64Url 变体使用 -、_),更适合作为环境变量存储。

(2) 防止误解密钥长度。 HS256 需要至少 256 位(32 字节)的密钥,直接用短字符串作为密钥可能不够安全。Base64 编码后,开发者可以确保密钥长度符合要求。

(3) 处理二进制数据。 有时候,secret 需要是二进制格式,但很多应用程序(如 .env 配置文件)通常以字符串存储密钥。Base64 可以把二进制密钥转换为字符串,方便存储和传输。

安全性

因为 JWT 的签名是服务器端生成的,所以,如果攻击者可以获取到服务器的密钥,那么攻击者就可以生成任意的 JWT,从而绕过服务器的认证。另外,JWT的payload中可能包含一些敏感信息,如用户名、密码、权限等,如果攻击者可以获取到这些信息,那么攻击者就可以进行一些恶意操作。

其他

  • 在软件售卖场景中,有把 jwt 作为许可证来使用的。即软件使用过程中不断去读取 jwt token 文件,如果验证出过期,则软件无法使用。与一般的登录场景类似的是,许可证也是由服务端(软件卖方)签发,每次使用都要验证一次。

Jason Lee
Hi, I'm Jason Lee

I hope I can still be curious about the world when I turn 60 years old.

Enjoy!

Contact me:

Email | GitHub


Content licenced under CC BY-NC-ND 4.0