JWT,是JSON Web Token的缩写,目前最流行的跨域认证解决方案。
为什么需要JWT?
一般的互联网用户认证流程如下:
- 用户向服务器发送用户名和密码。
- 服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
- 服务器向用户返回一个 session_id,写入用户的 Cookie。
- 用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
- 服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。
这就是经典的cookie-session认证模式,cookie保存在客户端,持有session-id,而session保存在服务端,持有用户信息和权限、过期时间等信息。
这种模式简洁易用,还能保证信息安全。
但是随着架构更新成服务器集群和跨域的服务导向架构,就要求实现session共享,否则不能实现session共享。一般来说,能够通过中间件实现session共享,比如redis。
举例来说,某系统有两个后端服务,都是集群部署,分别为服务A和服务B,用户在A服务登录之后,携带session-id请求B,要求用户能够通过B的登录验证,并且能获取用户信息。
大家都知道,session默认是存储的服务端的硬盘中,上面的场景中,用户登录的session应该会存储在服务集群A的某个节点上,那么用户访问任何其他的节点或服务都要重新登录。
最常见的解决方案就是利用中间件存储session。如上图中,若将session存储在redis中,那么用户登录之后,每个服务节点都能通过session-id查询到session,实现单点登录。
那么这种cookie-session模式有什么缺点呢?
- 客户端禁用cookie就会失效
- 跨域可能会导致cookie丢失
- 必须要有统一的中间件
- 中间件如果挂了,登录就会失效
优化方案
- 弃用cookie,直接将用户token存放在request header中,用户信息以token为key存放在redis
- 服务器索性不保存session数据了,直接将用户token和用户信息全都存在request header中,JWT就是此方案的代表
JWT的原理
怎么实现将用户信息存放在客户端,还能实现用户登录认证和权限认证?
我们可以将用户信息和用户权限和用户认证时间放一起生成一个json,如下
{
"姓名": "张三",
"角色": "管理员",
"到期时间": "2024年12月1日0点0分"
}
然后如果将这个字符串放在请求头中,那么服务端每次接收到请求都能知道用户信息和是否已登录等。
这样做的好处是服务器是无状态的,扩展性非常好。
但是请求头必须经过编码和网络传输,需要考虑乱码和URLEncode等问题。
所以我们要将上面的字符串使用Base64URL进行编码,规避这些问题。
新问题又来了,用户信息在客户端被篡改了怎么办?
我们再加一道签名(一种信息安全技术,此处不展开),防止信息篡改。
最终,上面的JSON经过这样魔改之后,就是所谓的JWT!它最终长这样:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyLlp5PlkI0iOiLlvKDkuIkiLCLop5LoibIiOiLnrqHnkIblkZgiLCLliLDmnJ_ml7bpl7QiOiIyMDI05bm0MTLmnIgx5pelMOeCuTDliIYifQ.fvBo5i2wXeYufnUQGLaJvtY6GdcqRhTgA5kc5S8HqLU
总结一下,JWT就是一串经过签名和编码的json对象,用户登录后由服务端生成,一般通过request header传输,轻松实现在不同平台和系统间信息交换和认证。
JWT官网: [https://jwt.io/]
JWT结构
JWT 有三个部分,依次如下。
Header(头部)