JSON Web令牌(JWT)
参考JWT
JWT与cookie+session的区别
服务器端维护登录状态,使用传统Cookie和Session方案,扩展性不好。而 JSON Web Token(JWT)可实现服务器无状态,扩展性好。
先解释一下单点登录的概念:在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任。单点登录在大型网站里使用得非常频繁,例如像阿里巴巴这样的网站,在网站的背后是成百上千的子系统,用户一次操作或交易可能涉及到几十个子系统的协作,如果每个子系统都需要用户认证,不仅用户会疯掉,各子系统也会为这种重复认证授权的逻辑搞疯掉。
在单点登录的时候,共享登录状态信息的实现有以下两种方案:
- 方案一:使用session 数据持久化(写入数据库或者Redis中),服务受到请求后,向持久层请求数据,但如果持久层挂了,就会单点失败。
- 方案二:使用JWT,服务器不保存session数据,所有状态信息都保存在客户端,每次请求都发回服务器。
JWT实现原理
客户端首次访问服务器,并且认证成功以后,生成一个json对象,发回给客户端。之后,客户端每次与服务端通信,都回传这个JSON对象。服务器靠这个JSON对象认证用户身份。为防止用户篡改数据,服务器生成这个对象的时候,会加上签名。
使用了JWT后,服务器不再保存任何session数据,实现了服务无状态,从而比较容易实现扩展。
JSON对象具体内容
Header.Payload.Signature
- Header:经Base64URL算法转成字符串的JSON对象,描述JWT的元数据,如签名算法(默认HMAC SHA256 对称加密)、令牌类型等属性。
- Payload:经Base64URL算法转成字符串的JSON对象,实际传输的数据,如签发人iss、主题sub、受众aud、过期时间exp、生效时间nbf、签发时间iat、编号jti (7个官方字段)以及自定义私有字段。
- Signature:对前两部分的签名,指定**secret ,对头部以及载荷内容进行签名。如果有人对头部以及载荷的内容解码以后进行修改,再进行编码的话,即时使用相同**签名,结果也与原来的不一样。如果不知道服务器加密时候用的秘钥的话,得出来的签名也是不一样的。
Base64URL算法
Base64编码后的字符串中可能包含 + 、/ 和 = 三个字符,在 URL 里面有特殊含义,所以要被替换掉:= 被省略、+ 替换成-,/ 替换成 _ 。这就是 Base64URL 算法。
JWT作为一个令牌(token),有些场景可能会附到URL中(如:api.example.com/?token=xxx),因此需要采用Base64URL 算法将Header 和 Payload 转化为字符串。
JWT优缺点
优点:
- 所有状态信息存储在客户端,减轻了服务端压力(不需要存储session)
- 可扩展性好,应用程序分布式部署的情况下,session需要做多机数据共享,存在数据库或者redis里面。而jwt不需要。
- JWT不仅可以用于认证,也可以在 JWT 中存储用户权限信息、用户个人信息。有效使用JWT,可以降低服务器查询数据库的次数(用户信息都写在JWT中,不需要访问数据库取用户信息)
缺点:
- 使用过程中无法废止或修改,签发后到期前始终有效
- 包含认证信息,泄露后权限被盗用(建议采用较短的有效期,并使用HTTPS协议传输)
- JWT 的内容(内部的 JSON 数据)通常是不加密的。这意味着,即使没有**,也可以查看 JWT 内的数据。JWT 默认并不会加密你的数据,它只是帮助你验证是你信任的一方创建了它。因此jwt中不能存储敏感数据。而session的信息是存在服务端的,相对来说更安全。
- 由于是无状态使用JWT,所有的数据都被放到JWT里,每一次http请求都会把jwt携带在Header里面,http请求的Header可能比Body还要大,通信开销大。
总结
适合使用jwt的场景:
- 有效期短
- 只希望被使用一次
比如,用户注册后发一封邮件让其**账户,通常邮件中需要有一个链接,这个链接需要具备以下的特性:能够标识用户,该链接具有时效性(通常只允许几小时之内**),不能被篡改以**其他可能的账户,一次性的。这种场景就适合使用jwt。
而由于jwt具有一次性的特性。单点登录和会话管理非常不适合用jwt,如果在服务端部署额外的逻辑存储jwt的状态,那还不如使用session。基于session有很多成熟的框架可以开箱即用,但是用jwt还要自己实现逻辑。