前言
上一篇介绍了如何配合 Swagger UI 解决写文档这个痛点,这篇将介绍如何利用 Redis 解决 JWT 登录认证的另一个痛点:同账号的登录挤出问题。(再不更新,读者就要寄刀片了 -_-||)
GitHub 项目地址,欢迎各位大佬 Star。
为了照顾还没学到第八课读者,本篇教程单独开了一个分支
use-redis
,拉项目后记得切换
前期准备
什么是 Redis
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。
Redis 的效率很高,官方给出的数据是 100000+ QPS,这是因为:
- Redis 完全基于内存,绝大部分请求是纯粹的内存操作,执行效率高。
- Redis 使用单进程单线程模型的(K,V)数据库,将数据存储在内存中,存取均不会受到硬盘 IO 的限制,因此其执行速度极快。
另外单线程也能处理高并发请求,还可以避免频繁上下文切换和锁的竞争,如果想要多核运行也可以启动多个实例。- 数据结构简单,对数据操作也简单,Redis 不使用表,不会强制用户对各个关系进行关联,不会有复杂的关系限制,其存储结构就是键值对,类似于 HashMap,HashMap 最大的优点就是存取的时间复杂度为 O(1)。
- Redis 使用多路 I/O 复用模型,为非阻塞 IO。
注:Redis 采用的 I/O 多路复用函数:epoll/kqueue/evport/select。
安装 Redis
要使用 Redis,那首先得安装 Redis,由于本篇的重点不在 Redis安装,这里贴上 Windows 和 MacOS 环境的安装教程,不再赘述:
有意思的是,官方的教程中提到了:
Redis 官方不建议在 windows 下使用 Redis,所以官网没有 windows 版本可以下载。还好微软团队维护了开源的 window 版本,虽然只有 3.2 版本,对于普通测试使用足够了。
Redis 可视化客户端
1. Mac OS
笔者使用 MacOS 系统,故使用 AnotherRedisDesktopManager 作为 Redis 可视化客户端:
1 | 复制代码# clone code |
2. Windows
在 Windows 下,可以使用 Redis Desktop Manager
官网的需要付费,不过测试同事用的 0.8.8.384
版本,读者可自行选择:
启动 Redis 并连接客户端
由于使用的 MacOS 系统,这里直接拿 AnotherRedisDesktopManager
做演示了,Windows 也是大同小异的。
我们先将 Redis 服务开起来,进入 /usr/local/bin/
(具体根据你的安装路径来定),输入下列命令:
1 | 复制代码$ redis-server |
出现下图表示服务启动成功:
然后新开一个终端,进入同样的目录,启动 Redis 客户端:
1 | 复制代码$ redis-cli |
使用客户端连接可能需要输入密码,我们先将它设好,这里涉及到 2 个指令
查看密码:
1 | 复制代码$ config get requirepass |
设置密码:
1 | 复制代码$ config set requirepass [new passward] |
下面是我的指令记录,因为设置了密码 root
,所以退出重进后需要 -a [密码]
,还有一点是,这种方式设置的密码,重启电脑后,原先设置会消失,需要重新设置
接下来启动 AnotherRedisDesktopManager
,启动方法在上文提到了,需要新开一个终端标签页启动 electron。
左上角点击【新建连接】,输入配置信息即可:
然后就可以看到总览了:
好了,终于可以步入文章正题了。
Nest 操作 Redis
1. Redis 连接配置
首先,编写 Redis 配置文件,这里就直接整合到 config/db.ts
中了:
1 | 复制代码// config/db.ts |
2. 建造 Redis 工厂
将这里需要配合 ioredis 使用:
1 | 复制代码$ yarn add ioredis -S |
添加成功后,我们需要编写一个生成 Redis 实例列表的文件:
1 | 复制代码// src/database/redis.ts |
因为 redis 可以同时存在多个库(公司的有 255 个,刚刚本地新建的有 15 个),故需要传入 db
进行区分,当然,也可以写死,但之后每使用一个库,就要新写一个 class
,从代码复用性上来说,这样设计很糟糕,所以在这里做了个整合。
函数里面的打印,是为了方便以后日志复盘,定位调用位置。
3. 调整 token 签发流程
在用户登录成功时,将用户信息和 token 存入 redis,并设置失效时间(单位:秒),正常情况应与 JWT 时效保持一致,这里为了调试方便,只写了 300 秒:
1 | 复制代码// src/logical/auth/auth.service.ts |
关于 Redis 的使用,文末附上了一些科普教程,如果学习过程中需要查指令,可以去这里查询: Redis 命令参考
4. 调整守卫策略
这里本来想新建一个 token.guard.ts
的,但后面感觉每个路由又全加一遍,很麻烦,故直接调整 rbac.guard.ts
:
1 | 复制代码// src/guards/rbac.guard.ts |
5. 验证
我们试着登录一下:
先看看日志,Redis 有没有被调用:
再看看 Redis 客户端里的记录:
发现已经将 token 存入了,并且到截图时,已经过去了 42 秒。
然后我们将 token 复制到请求商品列表的接口,请求:
上图是正常请求的样子,然后我们再登录,不修改这个接口的 token:
附上相关日志:
上图可以看到,策略已经生效了。
再看看 Redis 中记录到期会不会消失的情况,可以点击 TTL 旁边的绿色刷新键,查看剩余时间:
TTL 为 -2 就代表该键已到期,记录不存在了,我们可以点击左边的放大镜刷新一下:
注:TTL 为 -1 代表未设置过期时间(即一直存在);为 -2 表示该键不存在(即已失效)
可以看到,该条记录已经消失了,不再占用任何空间。
至此,大功告成。
总结
本篇介绍了如何在 Nest 中使用 Redis,并实现登录挤出的功能,稍稍弥补了 JWT 策略的缺陷。这里只是抛出一个“挤出”的思路,不局限于做在守卫上,如果有更好的思路,欢迎下方留言讨论。
利用 Redis 可以做很多事情,比如处理高并发,记录一些用户状态等。我曾经就用[队列]来处理红包雨活动,压测记录是 300+ 次请求/每秒。
还可以用来处理“登录超时”需求,比如把 JWT 的时效设置十天半个月的,然后就赋予 Redis 仅仅 1-2 个小时的时效,但是每次请求,都会重置过期时间,最后再判断这个键是否存在,来确认登录是否超时,具体实现就不在这里展开了,有兴趣的读者可自行完成。
本篇收录于NestJS 实战教程,更多文章敬请关注。
参考资料:
本文使用 mdnice 排版
本文转载自: 掘金