那这个 Token 是个什么样的呢?
RFC 并没有明确规定;
不过业内比较常见的方案是使用 JWT ;
JWT 是 JSON Web Token 的缩写;
要学习 JWT 我们可以拿 session 作为参照物;
在传统开发中我们在用户登录后会创建一个 session 文件 xxx ;
用户的 id 等信息就存储在 xxx 文件中;
接着会在 Set-Cookie
中返回 xxx 作为 session_id ;
以后前端请求的时候就会在 cookie 中携带 xxx ;
服务器端根据 xxx 从文件中读取用户信息;
从上面的流程我们可以看出要验证一个用户需要有两步;
- 传递 session 文件名
- 根据文件名从文件中获取用户信息
那我们可不可以不传文件名;
服务器端直接返回用户信息组成的认证字符串;
以后前端就把认证字符串发送给服务器端;
服务器端从认证字符串中获取用户信息;
这样只需要一步就可以验证用户;
不需要 session 文件的参与了;
但是这里面有两个问题需要解决;
- 怎么用字符串保存用户信息
- 如何防止篡改即服务器端如何信任前端发送的用户信息字符串是真实有效的;
既然本文的标题是 JWT ;
那是时候开始 JWT 的表演了;
下面就有请 JWT 来解决上面的问题;
JWT 定义这个认证字符串为三段;
Header(头部) . Payload(负载) . Signature(签名) ;
Header
头部是一个 JSON ;
{ "alg": "HS256", "typ": "JWT" }
JSON
Copy
字段 | 全称 | 描述 |
alg | algorithm | 是签名的算法;一般是 HS256 |
typ | type | 固定值为 JWT |
Payload
负载同样是一个 JSON ;
{ "iss": "d8b832c0c8caf0d99e9406ed", "sub": "1", "aud": "baijunyao", "iat": "1557066830", "nbf": "1557066840", "exp": "1557066850", "jti": "9e9668d8b8306ed8caf0d94" }
JSON
Copy
字段 | 全称 | 描述 |
iss | issuer | 发布者 |
sub | subject | 面向的用户; 一般是用户的 id |
aud | audience | 受众;比如说之前 OAuth 系列中的 client id |
iat | issued at | 签发时间的时间戳 |
nbf | not before | 生效时间的时间戳 |
exp | expiration time | 过期时间的时间戳 |
jti | jwt id | 每个 JWT 自己的唯一 id |
上面这些是 JWT 协议定义的信息;
负载中还可以存放自定义的信息;
Signature
签名的作用是保证头部和负载的信息不能被篡改;
默认使用 HS256 算法进行签名;
如果对 HS256 不了解的话可以先简单的把它约等于 MD5 加盐;
HS256 需要一个 Secret ;
这个 Secret 是保存在服务器端的不可以泄露;
否则别人就可以伪造 JWT 了;
签名的方式是把 头部和负载分别 base64UrlEncode
后用 .
拼接起来使用 Secret 进行 HS256 ;
Signature = HS256(base64UrlEncode(Header) . base64UrlEncode(Payload), Secret)
PHP
Copy
我从 jwt.io 上粘贴个例子;
至此头部、负载、签名我们已经凑齐了;
我们把 base64UrlEncode 的头部和负载以及签名用 .
拼接起来就是一个 JWT 了;
JWT = base64UrlEncode(Header) . base64UrlEncode(Payload) . HS256(base64UrlEncode(Header) . base64UrlEncode(Payload), Secret)
PHP
Copy
认真观察上面这个生成 JWT 的过程;
此处一个送命题;
敲黑板画重点了;
Secret 只是用于签名;
并没有用于加密;
JWT 中的 Header 和 Payload 是使用 base64UrlEncode 进行加密的;
任何人都可以非常轻松的就使用 base64UrlDecode 进行解密;
所以虽然 Payload 中可以存储自定义的信息;
但千万不能存储敏感信息;
JWT 是由服务器端生成返回给前端的;
前端在发送请求的时候一般把 JWT 放在 HTTP Headers 中的 Authorization 字段中;
Accept: */* Host: baijunyao.com Authorization: Bearer JWT_xxxx ...
Bash
Copy
终于讲的差不多了;
接着来总结下 JWT 的优缺点吧;
优点
- 相比于 session 少了读取文件的步骤,效率更高,方便扩展
- 因为不使用 cookie 天生免疫 CSRF 攻击
- 因为不使用 cookie 不用担心用户禁用 cookie ;不用悬挂本站需要使用 cookie 的提示信息;
- 适合 APP 前后端分离的场景
缺点
- 一旦签发不能撤销 没有类似清空 session 的操作
- 不能自动续签
针对上面缺点的解决方案;
- 使用黑名单机制; 把作废的 JWT 放入黑名单; 但是这样就引入了状态; 相比 session 就没有那么大的优势了;
- 使用 OAuth 中的 refresh_token