在web开发中,我们会把用户的登录信息存储在「session」里。而session是依赖于「cookie」的,即服务器创建session时会给它分配一个唯一的ID,并且在响应时创建一个cookie用于存储这个「sessionId」。当客户端收到这个cookie之后,就会自动保存这个sessionId,并且在下次访问时自动携带这个sessionId,届时服务器就可以通过这个sessionId得到与之对应的session,从而识别用户的身。如下图:
现在的互联网应用,基本都是采用分布式部署方式,即将应用程序部署在多台服务器上,并通过nginx做统一的请求分发。而服务器与服务器之间是隔离的,它们的session是「不共享」的,这就存在session「同步」的问题了,如下图:
如果客户端第一次访问服务器,请求被分发到了服务器A上,则服务器A会为该客户端创建session。如果客户端再次访问服务器,请求被分发到服务器B上,则由于服务器B中没有这个session,所以用户的身份无法得到验证,从而产生了「不一致」的问题。
解决这个问题的办法有很多,比如可以协调多个服务器,让他们的session保持同步。也可以在分发请求时做绑定处理,即将某一个IP固定分配给同一个服务器。但这些方式都比较麻烦,而且性能上也有一定的消耗。更合理的方式就是采用类似于Redis这样的高性能缓存服务器,来实现分布式session。
从上面的叙述可知,我们使用session保存用户的身份信息,本质上是要做两件事情:
- 第一是保存用户的身份信息;
- 第二是验证用户的身份信息。
如果利用其它手段实现这两个目标,那么就可以不用session,或者说我们使用的是广义上的session了。
具体实现的思路如下图,我们在服务端增加两段程序:
- 第一是「创建令牌」的程序,就是在用户初次访问服务器时,给它创建一个「唯一的身份标识」,并且使用cookie封装这个标识再发送给客户端。那么当客户端下次再访问服务器时,就会自动携带这个身份标识了,这和sessionId的道理是一样的,只是改由我们自己来实现了。另外,在返回令牌之前,我们需要将它存储起来,以便于后续的验证。而这个令牌是不能保存在服务器本地的,因为其他服务器无法访问它。因此,我们可以将其存储在服务器之外的一个地方,那么Redis便是一个理想的场所。
- 第二是「验证令牌」的程序,就是在用户再次访问服务器时,我们获取到了它之前的身份标识,那么我们就要验证一下这个标识是否存在了。验证的过程很简单,我们从Redis中尝试获取一下就可以知道结果。