Nestjs使用Redis的最佳实践
创始人
2024-12-14 03:05:56
0

在这里插入图片描述

前几天在项目中有用到Redis + JWT实现服务端对token的主动删除(退出登录功能)。故此介绍下如何在Nestjs中使用Redis,并做下总结。

知识准备

  1. 了解Redis - 网上很多简介。
  2. 了解Nestjs如何使用jwt生成token - 可移步看下我之前的文章

效果展示
在这里插入图片描述

一、mac安装与使用

示例代码用的本地的redis,所以介绍下mac如何安装redis和查看数据。

1. 安装

// 安装Redis brew install redis  // 启动Redis -(这将作为后台服务运行) brew services start redis // 或者,用redis-server命令+路径来启动(关闭终端服务停止) redis-server /usr/local/etc/redis.conf  // 验证Redis是否运行 (如果返回PONG,则表示Redis服务器正在运行) redis-cli ping   // 停止redis brew services stop redis 

2. mac使用 RedisInsight

官网下载 https://redis.io/insight/#insight-form
效果图如下
在这里插入图片描述

二、在nestjs中简单使用

1. 参考

  1. redis实现token过期:https://juejin.cn/post/7260308502031433786

2. 装包

pnpm install @nestjs/cache-manager cache-manager cache-manager-redis-yet redis -S  pnpm install @types/cache-manager -D 

3. 配置环境变量

  1. .env
# REDIS REDIS_HOST=localhost REDIS_PORT=6379 REDIS_DB=test // 本地没有密码 REDIS_PASSWORD=123456 # redis存储时的前缀 公司:项目:功能 REDIS_PREFIX=vobile:video-watermark-saas-node 

2. 配置config, src/config/config.ts

export default () => {   return {     // ....     redis: {       host: process.env.REDIS_HOST,       port: parseInt(process.env.REDIS_PORT, 10),       // username: process.env.DATABASE_USERNAME,       password: process.env.REDIS_PASSWORD,       database: process.env.REDIS_DB,       perfix: process.env.REDIS_PREFIX,     },   }; }; 

4. 创建目录使用

1. 创建目录

nest g resource modules/redis

2. 处理redis.module

import { Module, Global } from '@nestjs/common'; import { RedisService } from './redis.service'; import { RedisController } from './redis.controller'; import { CacheModule } from '@nestjs/cache-manager'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { redisStore } from 'cache-manager-redis-yet'; import type { RedisClientOptions } from 'redis';  @Global() // 这里我们使用@Global 装饰器让这个模块变成全局的 @Module({   controllers: [RedisController],   providers: [RedisService],   imports: [     CacheModule.registerAsync({       imports: [ConfigModule],       inject: [ConfigService],       useFactory: async (configService: ConfigService) => {         const store = await redisStore({           socket: {             host: configService.get('redis.host'),             port: configService.get('redis.port'),           },         });         return {           store,         };       },     }),   ],   exports: [RedisService], }) export class RedisModule { } 

3. 处理service

import { Inject, Injectable } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; import { formatSuccess } from 'src/util';  @Injectable() export class RedisService {   constructor(@Inject(CACHE_MANAGER) private readonly cacheManager: Cache) { }    async get(key: string): Promise {     return await this.cacheManager.get(key);   }    async set(key: string, value: any, ttl?: number): Promise {     console.log('set===');     const res = await this.cacheManager.set(key, value, ttl);     console.log('res1: ', res);     return res;   }    async testredis() {     const res = await this.set('aaa1', 'aaa', 60 * 60 * 1000);     console.log('res: ', res);     return formatSuccess('aa')   } } 

4. 处理controller

import { Controller, Get, Post, Body, Param } from '@nestjs/common'; import { RedisService } from './redis.service'; import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { Public } from '../auth/decorators/public.decorator';  @ApiTags('redis') @Controller('redis') export class RedisController {   constructor(private readonly redisService: RedisService) { }    // 测试redis   @ApiOperation({ summary: '测试redis', description: '测试redis' })   @Public()   @Post('testredis')   testredis() {     return this.redisService.testredis();   } } 

简单的配置到此结束,测试正常
在这里插入图片描述

三、进阶:退出登录token失效

背景:

  1. 当退出登录时jwt的token并未失效
  2. token无法被服务器主动作废

环境变量、创建目录参考上方。

下边展示核心

1. redis.service中增加删除功能

import { Inject, Injectable } from '@nestjs/common'; import { CACHE_MANAGER } from '@nestjs/cache-manager'; import { Cache } from 'cache-manager'; import { formatSuccess } from 'src/util';  @Injectable() export class RedisService {   constructor(@Inject(CACHE_MANAGER) private readonly cacheManager: Cache) { }    async get(key: string): Promise {     return await this.cacheManager.get(key);   }    async set(key: string, value: any, ttl?: number): Promise {     return await this.cacheManager.set(key, value, ttl);   }    // 删除   async del(key: string): Promise {     return await this.cacheManager.del(key);   }    async testredis() {     await this.set('aaa2', 'aaa', 60 * 60 * 1000);     return formatSuccess('ok');   } } 

2. 生成token时存入redis,退出登录删除token

auth.service,1.登录生成token后存入redis 2.退出登录删除redis里的token

import { Injectable, UnauthorizedException } from '@nestjs/common'; import { UserService } from '../user/user.service'; import * as md5 from 'md5'; import { JwtService } from '@nestjs/jwt'; import { formatError, formatSuccess } from 'src/util'; import { CreateUserDto } from '../user/dto/create-user.dto'; import { RedisService } from '../redis/redis.service' import { ConfigService } from '@nestjs/config';  @Injectable() export class AuthService {   constructor(     private userService: UserService,     private jwtService: JwtService,     private redisService: RedisService,     private configService: ConfigService,   ) { }    // 登录   async signIn(createUserDto: CreateUserDto): Promise {     const user: any = await this.userService.findOne(createUserDto.name);     if (!user) return formatError({ msg: 'The user does not exist' });     if (user?.password !== md5(createUserDto.password)) return formatError({ msg: 'wrong password' });     // 生成token     const payload = { id: user?.id, name: user?.name, password: user?.password };     const token = await this.jwtService.signAsync(payload);     // 将token存入redis      await this.redisService.set(`${this.configService.get('redis.perfix')}:token_${user?.id}`, token, 30 * 24 * 60 * 60 * 1000);     return formatSuccess({       token,       userInfo: {         id: user?.id,         name: user?.name,       },     });   }    // 退出登录   async logout(userid) {     this.redisService.del(`${this.configService.get('redis.perfix')}:token_${userid}`)     return formatSuccess('logout success')   } } 

添加退出登录接口
auth.controller.ts

  // 退出登录   @ApiOperation({ summary: '退出登录' })   @Post('logout')   logout(@Body() body: any, @Request() req: any) {     return this.authService.logout(req?.user?.id);   } 

3. jwt鉴权时比对redis里的token

修改:auth.guard.ts

// ... import { ConfigService } from '@nestjs/config'; import { RedisService } from '../redis/redis.service'  @Injectable() export class AuthGuard implements CanActivate {   constructor(     // ...     private readonly configService: ConfigService,     private redisService: RedisService,   ) { }    async canActivate(context: ExecutionContext): Promise {     const isPublic = this.reflector.getAllAndOverride(IS_PUBLIC_KEY, [       context.getHandler(),       context.getClass(),     ]);     if (isPublic) return true;     const request = context.switchToHttp().getRequest();     const token = this.extractTokenFromHeader(request);     console.log('token1111: ', token);     if (!token) throw new UnauthorizedException();     try {       const payload = await this.jwtService.verifyAsync(         token,         { secret: this.configService.get('jwt.secret') },       );       r       request['user'] = payload;     } catch {       throw new UnauthorizedException();     }     return true;   } } 

到此,大工告成。
在这里插入图片描述

相关内容

热门资讯

六分钟了解!神途免费辅助软件!... 六分钟了解!神途免费辅助软件!切实是有辅助方法(有挂助手)-哔哩哔哩1、神途免费辅助软件公共底牌简单...
七分钟了解!火神微信辅助!都是... 七分钟了解!火神微信辅助!都是是有辅助脚本(有挂细节)-哔哩哔哩1)火神微信辅助有没有挂:进一步探索...
7分钟了解!衢州都莱辅助器是真... 7分钟了解!衢州都莱辅助器是真是假!一贯存在有辅助方法(了解有挂)-哔哩哔哩进入游戏-大厅左侧-新手...
八分钟了解!玄龙辅助下载!竟然... 八分钟了解!玄龙辅助下载!竟然有辅助攻略(确实有挂)-哔哩哔哩运玄龙辅助下载辅助工具,进入游戏界面。...
十分钟了解!科乐天天踢起手好牌... 十分钟了解!科乐天天踢起手好牌!总是存在有辅助方法(确实有挂)-哔哩哔哩1、游戏颠覆性的策略玩法,独...
第一分钟了解!决战加血辅助!一... 第一分钟了解!决战加血辅助!一贯真的有辅助插件(有挂透视)-哔哩哔哩1、决战加血辅助透视辅助软件激活...
两分钟了解!欢聚水鱼科技辅助下... 两分钟了解!欢聚水鱼科技辅助下载!果然一直都是有辅助神器(有挂秘诀)-哔哩哔哩1、让任何用户在无需欢...
4分钟了解!微信小程序雀神挂件... 4分钟了解!微信小程序雀神挂件!真是真的有辅助插件(真的有挂)-哔哩哔哩1、完成微信小程序雀神挂件有...
两分钟了解!新上游通用挂是真的... 两分钟了解!新上游通用挂是真的吗!本来真的有辅助教程(有挂透视)-哔哩哔哩所有人都在同一条线上,像星...
四分钟了解!德州局脚本!一贯真... 四分钟了解!德州局脚本!一贯真的是有辅助神器(有挂细节)-哔哩哔哩1、上手简单,内置详细流程视频教学...