使用Redis管理登录令牌cookie

相关知识

大多数网站都会使用 cookie 记录用户的身份。cookie 是由少量数据组成的字符串(通常还要经过加密)。网站会要求浏览器存储这些数据,并在向服务端发起请求时将这些数据传回给服务端。

通常,用于处理登录(识别用户身份)的 cookie 分为两种:

  • 签名式 cookie
    • 存储包含用户 ID可直接识别用户的信息
    • 附加一个签名,核对 cookie 信息是否被恶意篡改
  • 令牌式 cookie
    • 存储一个随机字符串(令牌)
    • 通过在服务端的数据库中查找随机字符串和用户的对应关系识别用户身份

这两种 cookie 各有优缺,我们可以通过一个表格对比两者的优缺:

类型 优点 缺点
签名式 cookie 直接存储用户信息,方便验证用户身份;可以包含额外信息;对 cookie 进行签名较简单 遗漏签名会导致安全漏洞,加密方法不当会泄露用户敏感信息
令牌式 cookie cookie 体积小,可加快通信速度 需要使用数据库存储令牌,会造成额外开销

为了避免安全漏洞,本关卡中,我们使用令牌式 cookie

这篇文章将教会你掌握:1.如何核对令牌,2.如何更新令牌,3.如何定期清理无用信息。

如何核对令牌

前面提到了令牌式 cookie 的最大缺点就是会造成额外开销。大多数关系型数据库在每台数据库服务器上只能插入、更新或删除 200 - 2000 个数据行/秒。当网站的负载变高时,数据库就成为了瓶颈。所以,我们需要使用 Redis 取代关系型数据库,存放令牌。

首先,我们约定令牌的存储方式。我们使用一个哈希存储登录令牌与用户的映射关系,其中:

  • 哈希键名为 login
  • 登录令牌作为
  • 用户 ID 作为

所以核对令牌就变得十分简单,方法如下:

python 复制代码
import redis

conn = redis.Redis()

def check_token(token):
return conn.hget('login', token)

我们使用 hget() 方法从 Redis 中取出并返回令牌对应的用户 ID,而**当令牌不存在时,该方法则会返回 None**,从而达到核对检查的效果。

如何更新令牌

更新令牌需要做两个工作:

  • 记录用户令牌
  • 记录令牌生成时间

由于令牌存在被人窃取的可能,所以我们不允许令牌永不过期。通过记录令牌生成的时间戳,我们可以通过定期清理的方式,清理掉一定时间前生成(过老)的令牌,从而实现令牌的时限性,在一定程度上也减少了 Redis 的存储量,避免内存过高消耗。

使用有序集合 记录令牌生成时间能让我们更便捷的根据时间戳对令牌进行排序,然后再对一定时间前生成(过老)的令牌进行删除。将时间戳作为分值,令牌作为成员,记录到有序集合 recent:token 中:

python 复制代码
import time

timestamp = time.time() #返回当前时间的时间戳(1970年后经过的浮点秒数)
conn.zadd('recent:token', token, timestamp)

而记录用户令牌可以使用 hset() 方法,将 tokenuser_id 的域-值对关系记录到哈希中:

conn.hset('login', token, user_id)

上述两个操作是相关的 ,记录了用户令牌则应该也有对应的令牌生成时间,所以我们应该使用事务 将两条命令包起来,最后一起提交给 Redis 处理:

python 复制代码
def update_token(token, user_id):
timestamp = time.time()
pipe = conn.pipeline()
pipe.hset('login', token, user_id)
pipe.zadd('recent:token', token, timestamp)
pipe.execute()
如何定期清理无用信息

随着登录用户的增多,令牌存储所需的内存也会不断增加,这时,我们需要定期清理过期的令牌数据。

决定令牌的有效时间需要权衡数据安全与用户体验两方面:

  • 有效时间过短,则用户需要频繁的输入账户密码登入系统
  • 有效时间过长,则令牌泄露的可能性增大,伪造用户身份的可能性也越大

综合上述两个方面的考虑,我们将令牌的有效时间设置为一个星期(86400秒),在每次清理令牌数据时,我们找到令牌生成时间在一个星期前的数据,并将这些令牌和令牌生成时间数据全部删除。

寻找一个星期前生成的令牌是关键,我们可以使用当前 Unix 时间减去 86400 得到一个星期前的 Unix 时间戳,然后使用有序集合命令 ZRANGEBYSCORE 获取有序集合 recent:token 中所有分值(生成时间)大于等于 0,小于等于一个星期前的 Unix 时间戳的成员:

python 复制代码
one_week_ago_timestamp = time.time() - 86400

exipred_tokens = conn.zrangebyscore('recent:token', 0, one_week_ago_timestamp)

接下来,我们要从有序集合 recent:token 中删除掉 expired_tokens 的所有成员,以及从哈希 login 中移除所有过期的 token 域。

为了减少客户端与 Redis 之间的通信次数,我们可以直接使用 ZREMRANGEBYSCORE 命令移除有序集合 recent:token 中所有分值(生成时间)大于等于 0,小于等于一个星期前的 Unix 时间戳的成员:

python 复制代码
conn.zremrangebyscore('recent:token', 0, one_week_ago_timestamp)
conn.hdel('login', *expired_tokens)

我们使用了 hdel() 方法一次性从哈希 login 中移除了所有过期的 token 域,由于 expired_tokens 是一个数组,而客户端会默认 将输入的值转换为字符串 ,所以我们在这里要使用 *expired_tokens,以指针的形式 调用 expired_tokens 变量,传入多个参数(域)

让清理方法自动执行
你可以使用守护进程的方式来保证这个方法始终在运行,也可以通过**定时任务(cron job)**每隔一段时间执行一次
因为这些知识超出了本实训的讲解范围,在这里就不再详述。

编程要求

根据提示,在右侧Begin-End区域补充代码,完成令牌管理的后端处理逻辑:

  • check_token(token) 方法中:
    • 使用 hget() 方法从哈希 login 中取出参数 token 域的值
    • 返回(return)上述值
  • update_token(token, user_id) 方法中:
    • 参数说明:
      • token 为令牌
  • user_id 为该令牌对应的用户 ID
    • 获得当前时间并赋值给 timestamp
    • 使用事务 提交下列命令:
      • 将域 token 与值 user_id 对存入哈希键 login
  • 将成员 token 存入有序集合 recent:token 中,分值为 timestamp
  • clean_tokens() 方法中:
    • 使用当前时间减去 86400 得到一周前时间戳,并赋值给 one_week_ago_timestamp
    • 使用 zrangebyscore 方法获取有序集合 recent:token
      • 分值大于等于 0
  • 小于等于 one_week_ago_timestamp 的所有成员
  • 并赋值给变量 expired_tokens
    • 使用 zremrangebyscore 方法移除 有序集合 recent:token
      • 分值大于等于 0
  • 小于等于 one_week_ago_timestamp 的所有成员
    • 移除 哈希 login 中所有与变量 expired_tokens 中相同的域
      • 使用指针形式传入 参数 *expired_tokens
相关推荐
love530love13 分钟前
Windows 11 下 Z-Image-Turbo 完整部署与 Flash Attention 2.8.3 本地编译复盘
人工智能·windows·python·aigc·flash-attn·z-image·cuda加速
麒qiqi28 分钟前
理解 Linux IO 多路复用
开发语言·数据库
MediaTea1 小时前
Python:模块 __dict__ 详解
开发语言·前端·数据库·python
jarreyer1 小时前
python,numpy,pandas和matplotlib版本对应关系
python·numpy·pandas
山峰哥1 小时前
SQL调优核心战法——索引失效场景与Explain深度解析
大数据·汇编·数据库·sql·编辑器·深度优先
GottdesKrieges1 小时前
OMS迁移平台问题排查思路
数据库
代码or搬砖1 小时前
HashMap源码
开发语言·python·哈希算法
源力祁老师1 小时前
Odoo 客户端注册表
数据库
学Linux的语莫1 小时前
Milvus向量数据库的操作(基于Langchain)
数据库·langchain·milvus
怪我冷i2 小时前
dbeaver下载数据库驱动加速
数据库·postgresql·ai编程·ai写作