用 Redis 模拟 session
Redis 是一个强大而简单的键值型数据库,之前在公司网站改版的过程中大量使用 Redis 来处理一些特殊的需求,我希望能将自己对 Redis 的使用经验都分享出来,而这里算是第一篇吧。
背景
项目是一个有着不小访问量的网站,为了达到分流的作用,网站按照不同的业务(个人、企业、后台、营销、搜索、API等)区分成不同的子域名,而子域名下运行的是不同的实例。
对用户登录这块的逻辑,原来的做法是将登录的明文信息(登录ID、用户名、姓名等)在登录过程中直接写入用户 cookie,当需要进行登录校验的时候,后台直接取出 cookie 中保存的ID进行处理。可以想象,这样的模式只要模拟或者盗取了客户端的 cookie 信息,对于客户来说几乎没有任何安全性的保障。
由于用户的操作可能跨越多个实例,如果采用服务器 session 的机制的话,就需要解决 session 的共享问题,从技术的实现角度来说可能碰到的坑就更多了,于是我们利用了 Redis 来模拟服务器的 session。
实际设计
登录流程见图1
- 用户从浏览器中将登录信息传到 Web Server 中处理
- Web Server 首先跟进行用户信息验证,当验证通过的时候,根据用户的客户端信息(IP、浏览器信息等)进行散列,形成一个 token,这个 token 将会是 Redis 中的 Key,同时将经常需要获取的内容(用户ID、姓名等)组装成 Value,根据需要可以是 json 格式或者 HashTable,然后设定一个 expire time 保存进 Redis
- 将生成的 token 保存在用户的浏览器 cookie 中
验证流程见图2
- 用户访问需要进行验证登录的内容时,客户端会将 cookie 传到 Web Server 中
- Web Server 从 cookie 中获取到 token 的值,判断 token 是否存在于 Redis 中
- 若存在则将 Redis 中保存的信息返回到 Web Server 中进行处理,判断此次获取是否合法:
- 客户端的信息是否和生成 token 时的一致(IP、浏览器信息等)
- Value 中保存的内容是否和当前操作匹配(用户ID是否和当前处理的ID,或者如果将IP等信息放到内容中也可以将上一条的验证在这里处理)
- 所有验证通过则将正常的结果返回给用户,有需要的话还可以在这个过程中重置或延长原来 Redis key 的生存时间
这样做解决了什么问题?
其实这是一个很简单的思路,将客户登录验证和客户信息获取这两部分内容合并到 Redis 中来进行处理了
- 不利用服务器的 session,这样当多服务器部署的时候就不需要关心 session 的同步问题了
- 客户端 cookie 中保存的 token 信息是散列后的内容,不涉及任何业务属性,cookie 一旦被抓取到了单纯从 cookie 信息中也不会丢失任何敏感数据
- token 即使被盗用了并被有意进行模仿,如果不是完全按照客户登录时的IP、浏览器信息来进行模拟的话,基本无法获取到客户的信息
- 常用的信息保存在 Redis 中,由于直接读取了内存,比起持久化的数据库查询,读取速度快了很多,减少了负载同时提升了访问效率
- Redis 中的内容设定了生存时间,当有效期内执行操作重置时间这样的机制模拟了 session timeout,这样也能进一步保证用户的数据安全性