From 3bebef47ce1ba0d05ee892bde62d1962a89d5c45 Mon Sep 17 00:00:00 2001 From: J_Coder Date: Wed, 27 Nov 2024 18:29:13 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=90=9B=20fix:=20redis=20Key=EB=A5=BC?= =?UTF-8?q?=20=EC=BF=A0=ED=82=A4=EB=A1=9C=20=EB=84=A3=EC=9D=84=20=EA=B2=BD?= =?UTF-8?q?=EC=9A=B0=20Login=20=EB=9A=AB=EB=A6=BC=20=EB=AC=B8=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/admin/admin.service.ts | 2 +- server/src/common/guard/auth.guard.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/src/admin/admin.service.ts b/server/src/admin/admin.service.ts index 17b69f57..8608f455 100644 --- a/server/src/admin/admin.service.ts +++ b/server/src/admin/admin.service.ts @@ -36,7 +36,7 @@ export class AdminService { const sessionId = uuid.v4(); await this.redisService.redisClient.set( - sessionId, + `login:${sessionId}`, admin.loginId, `EX`, this.SESSION_TTL, diff --git a/server/src/common/guard/auth.guard.ts b/server/src/common/guard/auth.guard.ts index aaae434b..9d75b20b 100644 --- a/server/src/common/guard/auth.guard.ts +++ b/server/src/common/guard/auth.guard.ts @@ -14,7 +14,7 @@ export class CookieAuthGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); const sid = request.cookies['sessionId']; - const loginId = await this.redisService.redisClient.get(sid); + const loginId = await this.redisService.redisClient.get(`login:${sid}`); if (!loginId) { throw new UnauthorizedException('인증되지 않은 요청입니다.'); } From ff7263e95cc8b6176c20b3e73d1068e5464183f9 Mon Sep 17 00:00:00 2001 From: J_Coder Date: Wed, 27 Nov 2024 19:31:14 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A4=91=EB=B3=B5=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EB=B0=A9=EC=A7=80=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/admin/admin.controller.ts | 6 +++-- server/src/admin/admin.service.ts | 35 +++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/server/src/admin/admin.controller.ts b/server/src/admin/admin.controller.ts index 600b8dc5..b2d1ced5 100644 --- a/server/src/admin/admin.controller.ts +++ b/server/src/admin/admin.controller.ts @@ -5,12 +5,13 @@ import { HttpCode, HttpStatus, Post, + Req, Res, UseGuards, UsePipes, ValidationPipe, } from '@nestjs/common'; -import { Response } from 'express'; +import { Request, Response } from 'express'; import { AdminService } from './admin.service'; import { RegisterAdminDto } from './dto/register-admin.dto'; import { ApiTags } from '@nestjs/swagger'; @@ -35,8 +36,9 @@ export class AdminController { async loginAdmin( @Body() loginAdminDto: LoginAdminDto, @Res({ passthrough: true }) response: Response, + @Req() request: Request, ) { - await this.adminService.loginAdmin(loginAdminDto, response); + await this.adminService.loginAdmin(loginAdminDto, response, request); return ApiResponse.responseWithNoContent( '로그인이 성공적으로 처리되었습니다.', ); diff --git a/server/src/admin/admin.service.ts b/server/src/admin/admin.service.ts index 8608f455..b89d6ca7 100644 --- a/server/src/admin/admin.service.ts +++ b/server/src/admin/admin.service.ts @@ -3,7 +3,7 @@ import { Injectable, UnauthorizedException, } from '@nestjs/common'; -import { Response } from 'express'; +import { Response, Request } from 'express'; import { RegisterAdminDto } from './dto/register-admin.dto'; import { AdminRepository } from './admin.repository'; import * as bcrypt from 'bcrypt'; @@ -22,7 +22,12 @@ export class AdminService { private readonly redisService: RedisService, ) {} - async loginAdmin(loginAdminDto: LoginAdminDto, response: Response) { + async loginAdmin( + loginAdminDto: LoginAdminDto, + response: Response, + request: Request, + ) { + const cookie = request.cookies['sessionId']; const { loginId, password } = loginAdminDto; const admin = await this.loginRepository.findOne({ @@ -35,7 +40,31 @@ export class AdminService { const sessionId = uuid.v4(); - await this.redisService.redisClient.set( + if (cookie) { + this.redisService.redisClient.del(`login:${cookie}`); + } + + let cursor = '0'; + do { + const [newCursor, keys] = await this.redisService.redisClient.scan( + cursor, + 'MATCH', + 'login:*', + 'COUNT', + 100, + ); + + cursor = newCursor; + + for (const key of keys) { + const sessionValue = await this.redisService.redisClient.get(key); + if (sessionValue === loginId) { + await this.redisService.redisClient.del(key); + } + } + } while (cursor !== '0'); + + this.redisService.redisClient.set( `login:${sessionId}`, admin.loginId, `EX`, From 5649e674153d3f81adc1540fda1aaa09b13efb7d Mon Sep 17 00:00:00 2001 From: J_Coder Date: Wed, 27 Nov 2024 19:31:40 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=E2=9C=85=20test:=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EA=B0=92=20login=20prefix=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/test/statistic/today.e2e-spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/test/statistic/today.e2e-spec.ts b/server/test/statistic/today.e2e-spec.ts index 3195db28..165ca985 100644 --- a/server/test/statistic/today.e2e-spec.ts +++ b/server/test/statistic/today.e2e-spec.ts @@ -25,7 +25,7 @@ describe('Today view count statistic E2E Test : GET /api/statistic/today', () => email: 'test@test.com', rssUrl: 'https://test.com/rss', }), - redisService.redisClient.set('test1234', 'test'), + redisService.redisClient.set('login:test1234', 'test'), redisService.redisClient.zadd( redisKeys.FEED_TREND_KEY, '1', From 9eb5df6ff4d12124f6b6d7130666efe3a5940407 Mon Sep 17 00:00:00 2001 From: J_Coder Date: Wed, 27 Nov 2024 20:17:11 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=E2=9A=A1=EF=B8=8F=20perf:=20login=20-\>=20?= =?UTF-8?q?auth=20=EB=B3=80=EA=B2=BD,=20redis=20=ED=92=80=EC=8A=A4?= =?UTF-8?q?=EC=BA=94=20O(N)=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/admin/admin.service.ts | 24 ++++++++++++++++++------ server/src/common/guard/auth.guard.ts | 2 +- server/test/statistic/today.e2e-spec.ts | 2 +- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/server/src/admin/admin.service.ts b/server/src/admin/admin.service.ts index b89d6ca7..4fa134b6 100644 --- a/server/src/admin/admin.service.ts +++ b/server/src/admin/admin.service.ts @@ -41,31 +41,43 @@ export class AdminService { const sessionId = uuid.v4(); if (cookie) { - this.redisService.redisClient.del(`login:${cookie}`); + this.redisService.redisClient.del(`auth:${cookie}`); } let cursor = '0'; + let scanFlag = false; do { const [newCursor, keys] = await this.redisService.redisClient.scan( cursor, 'MATCH', - 'login:*', + 'auth:*', 'COUNT', 100, ); cursor = newCursor; - for (const key of keys) { - const sessionValue = await this.redisService.redisClient.get(key); + if (!keys.length) { + continue; + } + + const values = await this.redisService.redisClient.mget(keys); + + for (let i = 0; i < keys.length; i++) { + const sessionValue = values[i]; if (sessionValue === loginId) { - await this.redisService.redisClient.del(key); + await this.redisService.redisClient.del(keys[i]); + scanFlag = true; + break; } } + if (scanFlag) { + break; + } } while (cursor !== '0'); this.redisService.redisClient.set( - `login:${sessionId}`, + `auth:${sessionId}`, admin.loginId, `EX`, this.SESSION_TTL, diff --git a/server/src/common/guard/auth.guard.ts b/server/src/common/guard/auth.guard.ts index 9d75b20b..92efae2d 100644 --- a/server/src/common/guard/auth.guard.ts +++ b/server/src/common/guard/auth.guard.ts @@ -14,7 +14,7 @@ export class CookieAuthGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); const sid = request.cookies['sessionId']; - const loginId = await this.redisService.redisClient.get(`login:${sid}`); + const loginId = await this.redisService.redisClient.get(`auth:${sid}`); if (!loginId) { throw new UnauthorizedException('인증되지 않은 요청입니다.'); } diff --git a/server/test/statistic/today.e2e-spec.ts b/server/test/statistic/today.e2e-spec.ts index 165ca985..868b8c7e 100644 --- a/server/test/statistic/today.e2e-spec.ts +++ b/server/test/statistic/today.e2e-spec.ts @@ -25,7 +25,7 @@ describe('Today view count statistic E2E Test : GET /api/statistic/today', () => email: 'test@test.com', rssUrl: 'https://test.com/rss', }), - redisService.redisClient.set('login:test1234', 'test'), + redisService.redisClient.set('auth:test1234', 'test'), redisService.redisClient.zadd( redisKeys.FEED_TREND_KEY, '1',