也说 jwt
最近在一个 Nodejs 群里突然聊到后端怎么授权的问题,引出了 JWT 的使用问题。
- JWT 是什么?
- JWT 真的不需要在服务器端保存吗?
- JWT 和 OAuth2.0 中普通的 token 有什么区别?
我在网上搜索的时候发现下面奇葩的观点
阮一峰:另一种方案是服务器索性不保存 session 数据了,所有数据都保存在客户端,每次请求都发回服务器。JWT 就是这种方案的一个代表。[1]
我也是用过 JWT + OAuth2.0 实现过授权鉴权服务器的,而我的观点和上面的观点是不同的。
我不认同服务器不保存 session id 的做法,要说明原因,我们就需要知道 JWT 是什么。
JWT 是什么
这个问题可以直接看官网的介绍
我英文不好,但是大概能知道 JWT 是一个可以用 JSON 格式来传输数据的 web token,另外这个 token 是有数字签名的,使用 token 是能验证的,这让 token 具有比较好的可信度。
之所以在 token 前面强调 web,官网下面的签名算法就说明了一切:
1 | |
base64UrlEncode这个方法说明了生成的 JWT 是可用在 URL 上的。而且base64UrlEncode是可以 decode 的,这就使得payload中的内容可见。而签名是使用自身前面的header和payload的 base64 加上一个secret进行 hash 运算得来的,所以只要这个secret不泄漏,secret的拥有者就能检验数据是否被篡改过。
服务器端是否需要保存 JWT
先说我的答案,需要!
不管怎么样,客户端都是不可信的。服务器很难分辨请求是正常的用户操作还是黑客窃取,作为一个会写爬虫的 nodejs 程序员,深知服务器安全还是需要在服务器端来自己把控的。
不如做个简单的推理,就能很容易反驳阮一峰的说法,如下:
如果服务器端不保存任何状态信息,利用 JWT 的过期特性和签名特性,服务器在用户登录的情况下签发一个两个小时后过期的 JWT 给浏览器,这个 token 我们先称为 tokenA,但是用户在不到两个小时之内退出,并重新登录,服务器再次给用户签发一个两个小时后过期的 JWT 给浏览器,我们把后签发的称为 tokenB。因为 JWT 签发后是不能销毁的,即使是签发者也不能让签证提前过期。而这时候只要 tokenA 还处于有效期内,就会存在两个(甚至多个)同时生效的 token。这是很不安全的。虽然前端会说用户退出登录就在浏览器上销毁 token 了,但是谁能保证用户不会自己在退出登录之前先把 token 取出来呢?后端这是给自己留下安全隐患吗?
综上,在Authorization的应用中使用到 JWT,还是需要保存在服务器端,与之前保存session id的做法一样。
JWT 和普通的 Token 有什么区别
这里的讨论前提是在有Authorization的前提下进行的,这里的Authorization意味着用户想要得到 token,必须是以账户密码为前提获取的,即使是用 refresh token 换 access token 也是需要先登录。
有人可能觉得这个前提条件可以不设,我想说我就见过知道从 url 上得到的 username 去调用 userinfo 接口就能得到 token 的网站。下面的讨论在这个网站里不成立。
先说说在 JWT 之前,后端做 token 一般都是用用户特征值(唯一 id)加时间戳加后端持有的 secret 经过一定的算法生成的,然后放在 redis 里面做鉴权。得到的是一段字符串,前端也看不到授权的权限是什么,只能通过自己请求的接口和接口文档来确定这个 token 的权限访问。
这时候应该有人要发问了:
问: JWT 与以前的普通 token 有什么区别?
答:其实也没有什么很大的差别,毕竟 JWT 也只是一种 token,和普通的 token 相比,也只是payload可读,以及能在很大程度上保证payload不被篡改。当然要保证secret足够强大,尽量不是有意义的字符串。
问:token 不可读,这样能增加了安全性,JWT 把数据显式的展示出来,暴露过多的信息,这不是此地无银吗?
答:JWT 的确是显式的把payload的数据展示了,所以一般payload里面只放必要的数据。可能会觉得因为 header 里面声明了签名的 hash 算法,觉得比普通的 token 还容易破解出secret。其实也不必担心,因为 hash 算法不是加密算法,是没有解密一说的。签名只是为了保证payload是否被篡改,并不是对payload加密。
问:那和普通的 OAuth2.0 token 有什么区别,以前的 token 也是这么做的,JWT 就这?
答:把以前的 OAuth2.0 token 用 JWT 封装的,其实还是有一定的好处的,只要在payload里面增加过期时间,持有 JWT 端即可自己判断 token 是否还可用,是否需要重新刷新 token。但是不建议服务器端根据payload中的数据,把其他过多原本应该给签发端发送校验请求的工作转由自己去处理,因为如果这个 JWT 不是用非对称加密签发的,持有端是没办法判断 JWT 是否被篡改的。
总结
做Authorization不一定需要使用 JWT,以前那种普通的 token 和 JWT 都只是 token,JWT 只是多了一个payload可读,以及签名验证保证payload不被篡改,但是在不使用非对称加密的情况下,持有 JWT 端也没办法判断 JWT 是否被篡改,只能是签发端能判断。