赞
踩
前几天在项目中有用到Redis + JWT实现服务端对token的主动删除(退出登录功能)。故此介绍下如何在Nestjs中使用Redis,并做下总结。
知识准备
效果展示
示例代码用的本地的redis,所以介绍下mac如何安装redis和查看数据。
// 安装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
官网下载 https://redis.io/insight/#insight-form
效果图如下
pnpm install @nestjs/cache-manager cache-manager cache-manager-redis-yet redis -S
pnpm install @types/cache-manager -D
# REDIS
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=test
// 本地没有密码
REDIS_PASSWORD=123456
# redis存储时的前缀 公司:项目:功能
REDIS_PREFIX=vobile:video-watermark-saas-node
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,
},
};
};
nest g resource modules/redis
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<RedisClientOptions>({ imports: [ConfigModule], inject: [ConfigService], useFactory: async (configService: ConfigService) => { const store = await redisStore({ socket: { host: configService.get<string>('redis.host'), port: configService.get<number>('redis.port'), }, }); return { store, }; }, }), ], exports: [RedisService], }) export class RedisModule { }
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<T>(key: string): Promise<T> { return await this.cacheManager.get(key); } async set(key: string, value: any, ttl?: number): Promise<void> { 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') } }
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(); } }
简单的配置到此结束,测试正常
背景:
环境变量、创建目录参考上方。
下边展示核心
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<T>(key: string): Promise<T> { return await this.cacheManager.get(key); } async set(key: string, value: any, ttl?: number): Promise<void> { return await this.cacheManager.set(key, value, ttl); } // 删除 async del(key: string): Promise<void> { return await this.cacheManager.del(key); } async testredis() { await this.set('aaa2', 'aaa', 60 * 60 * 1000); return formatSuccess('ok'); } }
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<any> { 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);
}
修改: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<boolean> { const isPublic = this.reflector.getAllAndOverride<boolean>(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; } }
到此,大工告成。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。