why JWT
现在,前后端分离和 RESTful API 越来越火热,当后台渐渐开始只负责为客户端提供 API 接口之后,身份校验和接口安全成了难题。在传统的开发模式下,使用 cookie-session 可以保证接口安全,在没有登录的情况下访问关键数据会跳转到登录界面或者请求失败。而使用 REStful API 之后,cookie-session 存在以下 3 个问题:
- 客户端除了浏览器,可能还包括手机端 APP,对于手机端而言,管理 cookie 是一件麻烦的事情。
- RESTful 风格的 API 不建议使用 cookie。
- cookie 本身有一个缺陷,不能跨域。
正是存在上面的几个缺陷,现在 API 开始使用 JWT 代替 cookie-session 来做身份验证。
what JWT
JWT 全称 JSON Web Token。本质上 JWT 是一串 token 字符串。客户端登录之后,服务端返回一串 token 给客户端,之后每次客户端请求 API 接口都需要携带该 token 进行身份校验。JWT 由三个部分组成:头部(header)、载荷(payload)、签名(signature)。这三个部分使用 .
连接在一起就是一个完整的 JWT。所以,一个完整的 JWT 应该类似下面这种形式:
xxxxxx.yyyyy.zzzzz
header
header 是一个 json 数据,用于描述 JWT 的基本信息。一般要由两个部分组成:
- alg
- typ
alg 代表的是加密所使用的算法(后面会提到加密数据),typ 表示该 token 是什么类型的。这里 typ 自然是 JWT。
{
'alg':'HS256',
'typ':'JWT'
}
一个完整的 header 信息。最后使用 Base64 对 header 进行编码,得到 JWT 的第一部分。
payload
payload 是 JWT 存储信息的部分。payload 也是一个 json 数据,每一个 json 的 key-value 称为一个声明。
payload 有两种类型的声明:标准声明和自定义声明。
标准声明一共有 6 个,其名称和对应含义如下:
-
iss
: JWT 的签发者。 -
iat
: JWT 的签发时间,是一个 unix 时间戳。 -
exp
: JWT 的过期时间,是一个 unxi 时间戳。 -
aud
: 接受 JWT 的一方。 -
sub
: JWT 所面向的用户。 -
jti
: 唯一标识一个 JWT。
自定义声明为用户自己定义的 key-value,可以用来存储一些简单的基本信息。考虑到性能,不应该在 payload 中定义太多自定义声明。
定义一个 payload :
{
"iss": "jaychen",
"iat": 1441593502,
"exp": 1441594722,
"aud": "jaychen.cc",
"sub": "chenjiayaooo@gmail.com",
"jti:" "xxxxxxxx",
"user_id": "1",
"username": "jaychen"
}
上面这个 payload 中,id
和 username
为自定义声明。
有了 payload 只有,将该 payload 进行 Base64 加密,得到一串字符串之后,用 .
把 header 和 payload 连接起来。
signature
将 header 和 payload 两个部分连接起来之后,得到的字符串类似下面
xxxxx.yyyyy
接着,使用 header.alg
定义的加密算法对 hader.payload
的字符串进行加密,并且加密的时候应该有一个密钥。加密之后,得到一串加密字符串,最后把这串加密字符串也是用 .
拼接在 header.payload
后面,形成完整的 JWT。
这里签名的目的是为了保证 payload 数据的完整性。如果 JWT 在传输过程中被第三方劫持,中间人对 header.payload
进行修改,并且使用自己的密钥重新签名。服务端收到中间人修改过的 JWT,使用自己的密钥对 header.payload
进行再次加密,由于中间人和服务端使用的是不同的密钥签名,所以服务端再次加密的结果肯定和中间人加密的结果不一致,由此可以断定该 JWT 被恶意篡改。
基于 JWT 的身份验证
现在已经明白了 JWT 的生成过程,现在来梳理下 JWT 的使用流程。
- 首次登陆系统,向服务端发送 username&&password 进行登录。
- 服务端验证 username&&password,验证合法为客户端生成一串 JWT,这里在 payload 中可以自定义声明 user_id,username 等字段用来保存信息。
- 客户端收到服务端的 JWT 字符串,自行保存。后续需要请求 API 都要携带该 JWT 到服务端进行身份校验。
- 服务端收到客户端的 API 请求,先获取 JWT 信息,通过签名判断 JWT 的合法性,如果合法,返回数据。
JWT 的优点和注意事项
注意事项
上面生成 JWT 的过程中使用了 Base64 的加密算法对 payload 进行加密,Base64 是一种可逆的加密算法,这意味着其他人可以轻易的从加密结果中得到加密之前的信息,所以这注定了 payload 中不能保存密码之类的敏感信息。
优点
回顾上面生成 JWT 的步骤,payload 中我们保存了 user_id
和 username
这样的信息。在传统的 cookie-session 中,这些数据是服务端在 session 中维护的。JWT 把之前需要在服务端维护的 session 数据转移到客户端,使得服务端的压力小了很多。
JWT 本质只是一串字符串,所以可以无限制的使用各种姿势传递给服务端:当做 get 参数拼接在 URL 中、添加到 header 头部中、当做 post 参数传递。。。
如果客户端是手机 APP 等非浏览器客户端,那么使用 JWT 就可以免去对 cookie 的管理。
本文首发于:https://jaychen.cc
作者:jaychen
网友评论