JWT 认证
JSON Web Token 是目前最流行的跨域认证解决方案。JWT 也就是我们常用的 token。
跨域问题
互联网无法离开用户认证,用户认证的流程一般如下:
- 用户输入用户名和密码,提交到服务器
- 服务器验证用户名和密码,如果正确,在当前对话(session)中记录用户的登录状态
- 服务器返回一个包含用户信息(session_id)的 cookie 给浏览器
- 浏览器将 cookie 保存起来,以后每次请求都会带上这个 cookie,将这个 seesion_id 发送给服务器
- 服务器验证 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
它是一个很长的字符串,由三部分组成,中间用点分隔开,分别是:Header
、Payload
、Signature
。
Header
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
)
header
和 payload
都是经过 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 协议传输。