[nest.js] JWT strategy / guard

김민재·2025년 2월 14일

nest.js

목록 보기
30/63

사용 이유

1. JWT strategy 설정을 통해 Cookie에 있는 토큰을 가져와서 유저 정보를 반환해 준다.

2. Controller에 Guard를 적용해 req.user로 유저의 정보를 받아올 수 있다.

코드 jwt.strategy.ts (auth 폴더)

import { Strategy as JwtStrategy, ExtractJwt } from 'passport-jwt';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { UserService } from '../user/user.service';

// jwt 전략은 guard에서 사용가능
@Injectable()
export class JwtAuthStrategy extends PassportStrategy(JwtStrategy) {
    constructor(private usersService: UserService) {
        super({
            jwtFromRequest: ExtractJwt.fromExtractors([
                (reuest) => reuest?.cookies?.accessToken, // 쿠키에서 추출
            ]),

            secretOrKey: process.env.JWT_SECRET_KEY, // JWT 비밀키
        });
    }

    async validate(payload: any) {
        const user = await this.usersService.findUserId(payload._id);

        // 필요한 정보만 리턴
        return { _id: user._id, role: user.role }; // 필요한 최소 정보만 반환
    }
}

코드 auth.module.ts (auth 폴더) - app.module.ts 및 사용하는 곳에 적요

import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { JwtAuthStrategy } from './jwt.strategy';
import { JwtAuthGuard } from './auth.guard';
import { UserService } from '../user/user.service';
import { UserModule } from 'src/user/user.module';
import { AdminModule } from 'src/admin/admin.module';
import { JwtModule } from '@nestjs/jwt';
import { ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/user/entities/user.entity';
@Module({
    imports: [
        TypeOrmModule.forFeature([User]),
        PassportModule,
        UserModule,
        AdminModule,
        // jwt module 설정
        JwtModule.registerAsync({
            inject: [ConfigService],
            useFactory: (configService: ConfigService) => ({
                global: true,
                secret: configService.get<string>('JWT_SECRET_KEY'),
                signOptions: { expiresIn: '10m' },
            }),
        }),
    ],
    providers: [JwtAuthStrategy, JwtAuthGuard, UserService],
    exports: [JwtAuthGuard], // 다른 모듈에서 JwtAuthGuard를 사용할 수 있도록 export
})
export class AuthModule {}

코드 auth.guard.ts (auth 폴더)

import { Injectable, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { AuthGuard as NestAuthGuard } from '@nestjs/passport';
import { Observable } from 'rxjs';
import { Request } from 'express';

@Injectable()
export class JwtAuthGuard extends NestAuthGuard('jwt') {
    canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
        const request: Request = context.switchToHttp().getRequest();

        // 쿠키에서 JWT 토큰 추출
        const token = request.cookies['accessToken']; // 쿠키에서 토큰 가져오기

        if (token) {
            // 토큰이 있을 경우, 요청에 JWT 토큰을 넣어서 인증 절차를 진행할 수 있게 만듦
            request.headers['authorization'] = `Bearer ${token}`; // 헤더에도 토큰 심음, 나중에 한 번 해야함.
        } else {
            throw new UnauthorizedException('로그인을 진행해주세요.');
        }

        return super.canActivate(context); // Passport JWT 인증 전략을 실행
    }
}

코드 admin.guard.ts (auth 폴더)

import { Injectable, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { JwtAuthGuard } from './auth.guard'; // JwtAuthGuard 임포트

@Injectable()
export class AdminGuard extends JwtAuthGuard {
    canActivate(context: ExecutionContext): boolean {
        const request = context.switchToHttp().getRequest();

        // 요청에서 user 객체를 가져옵니다 (JwtAuthGuard에서 이미 설정됨)
        const user = request.user;

        // user가 없거나, role이 'admin'이 아니면 ForbiddenException 발생
        if (!user || user.role !== 'admin') {
            throw new ForbiddenException('관리자만 접근할 수 있습니다.');
        }

        return true;
    }
}
profile
개발 경험치 쌓는 곳

0개의 댓글