|
1 |
| -import { CanActivate, ExecutionContext, ForbiddenException, Injectable } from '@nestjs/common'; |
| 1 | +import { CanActivate, ExecutionContext, ForbiddenException, Inject, Injectable } from '@nestjs/common'; |
| 2 | +import Redis from 'ioredis'; |
2 | 3 |
|
3 | 4 | import { SessionsRepository } from '@sessions/sessions.repository';
|
4 | 5 | import { SessionsAuthRepository } from '@sessions-auth/sessions-auth.repository';
|
5 | 6 |
|
6 | 7 | @Injectable()
|
7 | 8 | export class SessionTokenValidationGuard implements CanActivate {
|
| 9 | + private readonly ttl = 300; // 5분 |
| 10 | + |
8 | 11 | constructor(
|
9 | 12 | private readonly sessionsRepository: SessionsRepository,
|
10 | 13 | private readonly sessionsAuthRepository: SessionsAuthRepository,
|
| 14 | + |
| 15 | + @Inject('REDIS_SESSION') private readonly sessionRedisClient: Redis, |
| 16 | + @Inject('REDIS_TOKEN') private readonly tokenRedisClient: Redis, |
11 | 17 | ) {}
|
12 | 18 |
|
13 | 19 | async validateSessionToken(sessionId: string, token: string) {
|
14 | 20 | if (!sessionId || !token) {
|
15 | 21 | throw new ForbiddenException('세션 ID와 사용자 토큰이 필요합니다.');
|
16 | 22 | }
|
17 | 23 |
|
18 |
| - const session = await this.sessionsRepository.findById(sessionId); |
19 |
| - if (!session) { |
20 |
| - throw new ForbiddenException('세션이 존재하지 않습니다.'); |
21 |
| - } |
22 |
| - |
| 24 | + const sessionKey = sessionId; |
| 25 | + const expiredDate = await this.sessionRedisClient.get(sessionKey); |
23 | 26 | const currentTime = new Date();
|
24 |
| - if (session.expiredAt && session.expiredAt < currentTime) { |
25 |
| - throw new ForbiddenException('세션이 만료되었습니다.'); |
| 27 | + |
| 28 | + if (!expiredDate) { |
| 29 | + const session = await this.sessionsRepository.findById(sessionId); |
| 30 | + if (!session) { |
| 31 | + throw new ForbiddenException('세션이 존재하지 않습니다.'); |
| 32 | + } |
| 33 | + if (session.expiredAt && session.expiredAt < currentTime) { |
| 34 | + throw new ForbiddenException('세션이 만료되었습니다.'); |
| 35 | + } |
| 36 | + await this.sessionRedisClient.set(sessionKey, session.expiredAt.toISOString(), 'EX', this.ttl); |
| 37 | + } else { |
| 38 | + const expiredDateTime = new Date(expiredDate); |
| 39 | + if (expiredDateTime < currentTime) { |
| 40 | + await this.sessionRedisClient.del(sessionKey); |
| 41 | + throw new ForbiddenException('세션이 만료되었습니다.'); |
| 42 | + } |
26 | 43 | }
|
27 | 44 |
|
28 |
| - const userSessionToken = await this.sessionsAuthRepository.findByToken(token); |
29 |
| - if (!userSessionToken || userSessionToken.sessionId !== sessionId) { |
| 45 | + const tokenKey = token; |
| 46 | + const sessionValue = await this.tokenRedisClient.get(tokenKey); |
| 47 | + if (!sessionValue) { |
| 48 | + const userSessionToken = await this.sessionsAuthRepository.findByToken(token); |
| 49 | + if (!userSessionToken || userSessionToken.sessionId !== sessionId) { |
| 50 | + throw new ForbiddenException('해당 세션에 접근할 권한이 없습니다.'); |
| 51 | + } |
| 52 | + |
| 53 | + await this.tokenRedisClient.set(tokenKey, userSessionToken.sessionId, 'EX', this.ttl); |
| 54 | + } else if (sessionValue !== sessionId) { |
30 | 55 | throw new ForbiddenException('해당 세션에 접근할 권한이 없습니다.');
|
31 | 56 | }
|
32 | 57 | }
|
|
0 commit comments