Post

JWT를 구현해보자(1)

NestJS를 이용해 JWT를 구현해보자

서론

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

사용할 라이브러리

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

1
npm install bcrypt @types/bcrypt

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

1
npm install @nestjs/jwt

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

1
npm install mysql2 typeorm @nestjs/typeorm

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

1
npm install @nestjs/config

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

1
npm install class-validator class-transformer

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

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

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

모듈 설정

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

1
2
3
4
5
6
7
8
9
10
11
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';

@Module({
    imports: [
        ConfigModule.forRoot({
            isGlobal: true,
        }),
    ],
})
export class ConfigurationModule { }

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import { Module, ValidationPipe } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { APP_PIPE } from '@nestjs/core';
import { ConfigurationModule } from './configuration.module';
import { JwtService } from '@nestjs/jwt';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: process.env.DB_HOST,
      port: parseInt(process.env.DB_PORT),
      username: process.env.DB_USERNAME,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME,
      entities: ['dist/entities/**/*.js'],
      synchronize: process.env.DB_SYNCHRONIZE === 'true',
    }),
    ConfigurationModule,
  ],
  controllers: [AppController],
  providers: [
    AppService,
    JwtService,
    {
      provide: APP_PIPE,
      useClass: ValidationPipe,
    },
  ],
})
export class AppModule { }

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

1
nest g mo auth

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from 'src/entities/user.entity';
import { UserSecurity } from 'src/entities/user.security.entity';
import { AuthController } from './auth.controller';
import { UserDeleted } from 'src/entities/user.deleted.entity';
import { ConfigurationModule } from 'src/configuration.module';

@Module({
    imports: [
        PassportModule,
        JwtModule.register({
            secret: process.env.SECRET_KEY,
            signOptions: { expiresIn: '60m' },
        }),
        TypeOrmModule.forFeature([User, UserSecurity, UserDeleted]),
        ConfigurationModule,
    ],
    providers: [AuthService],
    controllers: [AuthController],
})
export class AuthModule { }

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

JWT 전략 설정

1
2
3
4
5
//payload.interface.ts
export interface Payload {
    email: string;
    sub: string;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//jwt.strategy.ts
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { Payload } from './paylode.interface';
import { AuthService } from '../auth.service';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
    constructor(private authService: AuthService) {
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            secretOrKey: process.env.SECRET_KEY,
        });
    }

    async validate(payload: Payload) {
        const user = await this.authService.JwtValidateUser(payload);
        if (!user) {
            throw new UnauthorizedException();
        }
        return user;
    }
}

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

마치며

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

This post is licensed under CC BY 4.0 by the author.