回顾一下【Nest
入门系列文章】
前面Nest.js
系列的文章中我们其实留了两个可以用redis
优化的地方:
JWT
已经实现了服务端生成token
以及验证客户端发送的token
信息。JWT token
实现方式, 将基本信息直接放在token
中,以便于分布式系统使用, 但是我们没有设置有限期(这个是可以实现的),并且服务端无法主动让token
失效。而Redis天然支持过期时间,也能实现让服务端主动使token
过期。
当然并不是说JWT token 不如 redis+token实现方案好, 具体看使用的场景,这里我们并不讨论二者孰优孰劣,只是提供一种实现方案,让大家知道如何实现。
对于前端的小伙伴来说,Redis可能相对比较陌生,首先认识一下
Redis是一个开源(BSD许可)的,基于内存的数据结构存储系统,它可以用作数据库、缓存和消息中间件,是现在最受欢迎的 NoSQL 数据库之一。
其具备如下特性:
速度快
单节点读110000次/s,写81000次/s
基于内存运行,性能高效
用 C 语言实现,离操作系统更近
持久化
数据的更新将异步地保存到硬盘(RDB 和 AOF
多种数据结构
不仅仅支持简单的 key-value 类型数据
还支持:字符串、hash、列表、集合、有序集合
支持多种编程语言等等
缓存
缓存可以说是Redis最常用的功能之一了, 合理的缓存不仅可以加快访问的速度,也可以减少后端数据库的压力。
排行系统
利用Redis的列表和有序集合的特点,可以制作排行榜系统,而排行榜系统目前在商城类、新闻类、博客类等等,都是比不可缺的。
计数器应用
计数器的应用基本和排行榜系统一样,都是多数网站的普遍需求,如视频网站的播放计数,电商网站的浏览数等等,但这些数量一般比较庞大,如果存到关系型数据库,对MySQL或者其他关系型数据库的挑战还是很大的,而Redis基本可以说是天然支持计数器应用。
(视频直播)消息弹幕
直播间的在线用户列表,礼物排行榜,弹幕消息等信息,都适合使用Redis中的SortedSet结构进行存储。
例如弹幕消息,可使用ZREVRANGEBYSCORE
排序返回,在Redis5.0中,新增了zpopmax
,zpopmin
命令,更加方便消息处理。
Redis的应用场景远不止这些,Redis对传统磁盘数据库是一个重要的补充,是支持高并发访问的互联网应用必不可少的基础服务之一。
纸上谈兵终觉浅,必须实战一波~
Redis的安装和简单使用,我这里就不一一介绍了,这里贴上我之前写的两篇文章:
可以快速的安装、了解Redis数据类型以及常用的命令。
在Windows下使用 RedisClient, 在mac下可以使用Redis Desktop Manager
RedisClient下载链接:https://github.com/caoxinyu/RedisClient
下载后直接双击redisclient-win32.x86.2.0.exe
文件运行即可
image.png
启动后, 点击server -> add
image.png
连接后就可以看到总体情况了:
image.png
与SQL型数据不同,redis没有提供新建数据库的操作,因为它自带了16(0-15)个数据库(默认使用0库)。在同一个库中,key是唯一存在的、不允许重复的,它就像一把“密钥”,只能打开一把“锁”。键值存储的本质就是使用key来标识value,当想要检索value时,必须使用与value对应的key进行查找.
Redis认识作为文章前置条件,到这里及结束了, 接下来进入正题~
本文主要使用Redis实现缓存功能。
版本情况:
库 | 版本 |
---|---|
Nest.js | V8.1.2 |
项目是基于Nest.js 8.x
版本,与Nest.js 9.x
版本使用有所不同, 后面的文章专门整理了两个版本使用不同点的说明, 以及如何从V8
升级到V9
, 这里就不过多讨论。
首先,我们在Nest.js项目中连接Redis, 连接Redis需要的参数:
REDIS_HOST:Redis 域名
REDIS_PORT:Redis 端口号
REDIS_DB:Redis 数据库
REDIS_PASSPORT:Redis 设置的密码
将参数写入.env
与.env.prod
配置文件中:
image.png
使用Nest官方推荐的方法,只需要简单的3个步骤:
1 . 引入依赖文件
npm install cache-manager --save
npm install cache-manager-redis-store --save
npm install @types/cache-manager -D
Nest
为各种缓存存储提供统一的API,内置的是内存中的数据存储,但是也可使用 cache-manager
来使用其他方案, 比如使用Redis
来缓存。
为了启用缓存, 导入ConfigModule
, 并调用register()
或者registerAsync()
传入响应的配置参数。
2 . 创建module文件src/db/redis-cache.module.ts
, 实现如下:
import { ConfigModule, ConfigService } from '@nestjs/config';
import { RedisCacheService } from './redis-cache.service';
import { CacheModule, Module, Global } from '@nestjs/common';
import * as redisStore from 'cache-manager-redis-store';
@Module({
imports: [
CacheModule.registerAsync({
isGlobal: true,
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => {
return {
store: redisStore,
host: configService.get('REDIS_HOST'),
port: configService.get('REDIS_PORT'),
db: 0, //目标库,
auth_pass: configService.get('REDIS_PASSPORT') // 密码,没有可以不写
};
},
}),
],
providers: [RedisCacheService],
exports: [RedisCacheService],
})
export class RedisCacheModule {}
CacheModule
的registerAsync
方法采用 Redis Store 配置进行通信store
属性值redisStore
,表示'cache-manager-redis-store' 库isGlobal
属性设置为true
来将其声明为全局模块,当我们将RedisCacheModule
在AppModule
中导入时, 其他模块就可以直接使用,不需要再次导入registerAsync()
方法来处理异步数据,如果是静态数据, 可以使用register
3 . 新建redis-cache.service.ts
文件, 在service实现缓存的读写
import { Injectable, Inject, CACHE_MANAGER } from '@nestjs/common';
import { Cache } from 'cache-manager';
@Injectable()
export class RedisCacheService {
constructor(
@Inject(CACHE_MANAGER)
private cacheManager: Cache,
) {}
cacheSet(key: string, value: string, ttl: number) {
this.cacheManager.set(key, value, { ttl }, (err) => {
if (err) throw err;
});
}
async cacheGet(key: string): Promise<any> {
return this.cacheManager.get(key);
}
}
接下来,在app.module.ts
中导入RedisCacheModule
即可。
我们借助redis来实现token过期处理、token自动续期、以及用户唯一登录。
在登录时,将jwt生成的token,存入redis,并设置有效期为30分钟。存入redis的key由用户信息组成, value是token值。
// auth.service.ts
async login(user: Partial<User>) {
const token = this.createToken({
id: user.id,
username: user.username,
role: user.role,
});
+ await this.redisCacheService.cacheSet(
+ `${user.id}&${user.username}&${user.role}`,
+ token,
+ 1800,
+ );
return { token };
}
在验证token时, 从redis中取token,如果取不到token,可能是token已过期。
// jwt.strategy.ts
+ import { RedisCacheService } from './../core/db/redis-cache.service';
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
private readonly authService: AuthService,
private readonly configService: ConfigService,
+ private readonly redisCacheService: RedisCacheService,
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: configService.get('SECRET'),
+ passReqToCallback: true,
} as StrategyOptions);
}
async validate(req, user: User) {
+ const token = ExtractJwt.fromAuthHeaderAsBearerToken()(req);
+ const cacheToken = await this.redisCacheService.cacheGet(
+ `${user.id}&${user.username}&${user.role}`,
+ );
+ if (!cacheToken) {
+ throw new UnauthorizedException('token 已过期');
+ }
const existUser = await this.authService.getUser(user);
if (!existUser) {
throw new UnauthorizedException('token不正确');
}
return existUser;
}
}
当用户登录时,每次签发的新的token,会覆盖之前的token, 判断redis中的token与请求传入的token是否相同, 不相同时, 可能是其他地方已登录, 提示token错误。
// jwt.strategy.ts
async validate(req, user: User) {
const token = ExtractJwt.fromAuthHeaderAsBearerToken()(req);
const cacheToken = await this.redisCacheService.cacheGet(
`${user.id}&${user.username}&${user.role}`,
);
if (!cacheToken) {
throw new UnauthorizedException('token 已过期');
}
+ if (token != cacheToken) {
+ throw new UnauthorizedException('token不正确');
+ }
const existUser = await this.authService.getUser(user);
if (!existUser) {
throw new UnauthorizedException('token不正确');
}
return existUser;
}
实现方案有多种,可以后台jwt生成access_token
(jwt有效期30分钟)和refresh_token
, refresh_token
有效期比access_token
有效期长,客户端缓存此两种token, 当access_token
过期时, 客户端再携带refresh_token
获取新的access_token
。这种方案需要接口调用的开发人员配合。
我这里主要介绍一下,纯后端实现的token自动续期
实现流程:
设置jwt生成的token, 用不过期, 这部分代码是在auth.module.ts
文件中, 不了解的可以看文章 [Nest.js 实战系列第二篇-实现注册、扫码登陆、jwt认证]
// auth.module.ts
const jwtModule = JwtModule.registerAsync({
inject: [ConfigService],
useFactory: async (configService: ConfigService) => {
return {
secret: configService.get('SECRET', 'test123456'),
- signOptions: { expiresIn: '4h' }, // 取消有效期设置
};
},
});
然后再token认证通过后,重新设置过期时间, 因为使用的cache-manager
没有通过直接更新有效期方法,通过重新设置来实现:
// jwt.strategy.ts
async validate(req, user: User) {
const token = ExtractJwt.fromAuthHeaderAsBearerToken()(req);
const cacheToken = await this.redisCacheService.cacheGet(
`${user.id}&${user.username}&${user.role}`,
);
if (!cacheToken) {
throw new UnauthorizedException('token 已过期');
}
if (token != cacheToken) {
throw new UnauthorizedException('token不正确');
}
const existUser = await this.authService.getUser(user);
if (!existUser) {
throw new UnauthorizedException('token不正确');
}
+ this.redisCacheService.cacheSet(
+ `${user.id}&${user.username}&${user.role}`,
+ token,
+ 1800,
+ );
return existUser;
}
到此,在Nest中实现token过期处理、token自动续期、以及用户唯一登录都完成了, 退出登录时移除token比较简单就不在这里一一上代码了。
在Nest中除了使用官方推荐的这种方式外, 还可以使用nestjs-redis
来实现,如果你存token时, 希望存hash
结构,使用cache-manager-redis-store
时,会发现没有提供hash
值存取放方法(需要花点心思去发现)。
注意:如果使用
nest-redis
来实现redis缓存, 在Nest.js 8 版本下会报错, 小伙伴们可以使用@chenjm/nestjs-redis
来代替, 或者参考 issue上的解决方案:Nest 8 + redis bug。
源码地址:https://github.com/koala-coding/nest-blog
Nest.js系列目的:
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/yKm_U9Veh_1twIoXzM8Prw
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。