목록으로
NestJS

JWT를 구현해보자(1)

Dev Park
3/17/2023
21 views

서론

저번 게시글에서는 JWT에 대해서 알아보았다.
JWT를 NestJS를 통해 로그인 시스템을 만들고 JWT로 인증하는 프로젝트를 직접 구현해보자.

사용할 라이브러리

사용할 라이브러리는 아래와 같다.

npm install bcrypt @types/bcrypt

우선 단방향 암호화를 위한 라이브러리인 bcrypt를 설치해준다.
비밀번호 보안을 위한 암호화 알고리즘을 제공하는 라이브러리이다.

npm install @nestjs/jwt

nestjs에서 JWT를 구현해줄 라이브러리이다.
설정한 키값으로 인코딩을 진행해주고, 요청이 들어올경우 토큰을 검사하여 맞는 사용자를 인증해주는 라이브러리이다.

npm install mysql2 typeorm @nestjs/typeorm

TypeORM을 이용해 데이터베이스를 다룰 것이고, 사용할 데이터베이스는 mysql 이다.

npm install @nestjs/config

nestJS env를 이용해 환경변수를 사용하기위한 라이브러리이다.

npm install class-validator class-transformer

DTO를 사용하기 위한 라이브러리들이다.

npm install @nestjs/passport passport @types/passport-local passport-local

인증 프로세스를 처리하기위한 passport 라이브러리를 설치한다.

모듈 설정

우선 환경변수를 사용하기위해 'configuration.module.ts' 라는 파일을 만들어준다.

typescript
1import { Module } from '@nestjs/common'; 2import { ConfigModule } from '@nestjs/config'; 3 4@Module({ 5 imports: [ 6 ConfigModule.forRoot({ 7 isGlobal: true, 8 }), 9 ], 10}) 11export class ConfigurationModule { }

이 파일을 'app.module.ts' 파일에 import 해주면 환경변수를 사용할 준비는 끝난 것이다.
그 외로 DTO를 사용하기위한 APP_PIPE, TypeORM을 사용하기위한 라이브러리들을 import 해주면 된다.
또한 JWT를 사용하기위해 JwtModule, JwtService 를 import 해준다.

typescript
1import { Module, ValidationPipe } from '@nestjs/common'; 2import { AppController } from './app.controller'; 3import { AppService } from './app.service'; 4import { TypeOrmModule } from '@nestjs/typeorm'; 5import { APP_PIPE } from '@nestjs/core'; 6import { ConfigurationModule } from './configuration.module'; 7import { JwtService } from '@nestjs/jwt'; 8 9@Module({ 10 imports: [ 11 TypeOrmModule.forRoot({ 12 type: 'mysql', 13 host: process.env.DB_HOST, 14 port: parseInt(process.env.DB_PORT), 15 username: process.env.DB_USERNAME, 16 password: process.env.DB_PASSWORD, 17 database: process.env.DB_NAME, 18 entities: ['dist/entities/**/*.js'], 19 synchronize: process.env.DB_SYNCHRONIZE === 'true', 20 }), 21 ConfigurationModule, 22 ], 23 controllers: [AppController], 24 providers: [ 25 AppService, 26 JwtService, 27 { 28 provide: APP_PIPE, 29 useClass: ValidationPipe, 30 }, 31 ], 32}) 33export class AppModule { }

위와 같이 설정하면 Jwt를 사용하기위한 Module 세팅은 완료되었다.
이제 로그인을 담당할 module을 세팅해보자.

nest g mo auth

위의 명령어를 이용해 'auth.module.ts' 라는 파일을 만든다.

typescript
1import { Module } from '@nestjs/common'; 2import { AuthService } from './auth.service'; 3import { PassportModule } from '@nestjs/passport'; 4import { JwtModule } from '@nestjs/jwt'; 5import { TypeOrmModule } from '@nestjs/typeorm'; 6import { User } from 'src/entities/user.entity'; 7import { UserSecurity } from 'src/entities/user.security.entity'; 8import { AuthController } from './auth.controller'; 9import { UserDeleted } from 'src/entities/user.deleted.entity'; 10import { ConfigurationModule } from 'src/configuration.module'; 11 12@Module({ 13 imports: [ 14 PassportModule, 15 JwtModule.register({ 16 secret: process.env.SECRET_KEY, 17 signOptions: { expiresIn: '60m' }, 18 }), 19 TypeOrmModule.forFeature([User, UserSecurity, UserDeleted]), 20 ConfigurationModule, 21 ], 22 providers: [AuthService], 23 controllers: [AuthController], 24}) 25export class AuthModule { }

인증프로세스를 위한 PassportModule과 Jwt 를 사용하기위한 JwtModule를 각각 import 해준다.
그리고 Jwt의 인증토큰을 암호화하기위한 SECRET_KEY를 환경변수로 설정한다.
또한 토큰이 탈취당했을때, 이를 계속 사용할 수 없도록 토큰의 만료시간을 설정한다.
나는 60분으로 설정했다.
TypeORM에서 사용할 Entity들을 설정해주고, 환경변수 사용을위한 ConfigurationModule 또한 import 해준다.

JWT 전략 설정

typescript
1//payload.interface.ts 2export interface Payload { 3 email: string; 4 sub: string; 5}
typescript
1//jwt.strategy.ts 2import { Injectable, UnauthorizedException } from '@nestjs/common'; 3import { PassportStrategy } from '@nestjs/passport'; 4import { ExtractJwt, Strategy } from 'passport-jwt'; 5import { Payload } from './paylode.interface'; 6import { AuthService } from '../auth.service'; 7 8@Injectable() 9export class JwtStrategy extends PassportStrategy(Strategy) { 10 constructor(private authService: AuthService) { 11 super({ 12 jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 13 secretOrKey: process.env.SECRET_KEY, 14 }); 15 } 16 17 async validate(payload: Payload) { 18 const user = await this.authService.JwtValidateUser(payload); 19 if (!user) { 20 throw new UnauthorizedException(); 21 } 22 return user; 23 } 24}

위와 같이 payload 즉 인증을 위한 interface를 만든다.
그리고 jwt를 토큰으로 만들고, 클라이언트가 보낸 jwt가 제대로된 토큰인지를 인증하는 메소드를 만든다.
secretOrKey 는 환경변수로 설정해 외부에 유출되지 않도록 한다.

마치며

오늘은 JWT로 로그인 시스템을 만들기위한 Module과 JWT 전략을 설정해보았다.
코드가 길어질 것 같아 몇개의 게시물로 나눠서 작성할 예정이다.