[Nestjs] #6. JWT(JSON Web Token) 활용

exoluse·2021년 12월 6일
0

Nestjs

목록 보기
7/7
post-thumbnail

nestjs+jwt 자료를 구글링 하면서

느낀점은 단 하나... 공식사이트가 짱이다. nestjs가 궁금하다면 공식사이트로 ㄱ~

필요 패키지 설치

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

auth, user 모듈, 서비스 생성

nest g module auth
nest g service auth

user 모듈/서비스 구성

nest g module users
nest g service users

users.service.ts

대충 훑어보면 사용자 2명이 있고 그중에 1명을 찾는 메서드를 포함한다.


import { Injectable } from '@nestjs/common';

// This should be a real class/interface representing a user entity
export type User = any;

@Injectable()
export class UsersService {
  private readonly users = [
    {
      userId: 1,
      username: 'john',
      password: 'changeme',
    },
    {
      userId: 2,
      username: 'maria',
      password: 'guess',
    },
  ];

  async findOne(username: string): Promise<User | undefined> {
    return this.users.find(user => user.username === username);
  }
}

users.module.ts

UsersService를 @Module 데코레이터 내보내기 열에 포함

import { Module } from '@nestjs/common';
import { UsersService } from './users.service';

@Module({
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

auth 모듈/서비스 구성

auth.service.ts

사용자 검색 및 암호 확인 작업을 해준다.

import { Injectable } from '@nestjs/common';
import { UsersService } from '../users/users.service';

@Injectable()
export class AuthService {
  constructor(private usersService: UsersService) {}

  async validateUser(username: string, pass: string): Promise<any> {
    const user = await this.usersService.findOne(username);
    if (user && user.password === pass) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }
}

auth.module.ts

UsersModule을 import 해준다.

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';

@Module({
  imports: [UsersModule],
  providers: [AuthService],
})
export class AuthModule {}

로컬 인증 전략

auth/local.strategy.ts 추가


import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
  constructor(private authService: AuthService) {
    super();
  }

  async validate(username: string, password: string): Promise<any> {
    const user = await this.authService.validateUser(username, password);
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}

정의된 인증 전략 사용

auth.module.ts 수정

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '@nestjs/passport';

// 로컬 인증 전략 추가됨.
import { LocalStrategy } from './local.strategy';

@Module({
  imports: [UsersModule, PassportModule],
  
  // 프로바이더에 전략 추가
  providers: [AuthService, LocalStrategy],
})
export class AuthModule {}

라우팅 설정

app.controller.ts 수정

import { Controller, Request, Post, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Controller()
export class AppController {

  // 패스포트-로컬 전략을 확장할 때 @nestjs/passport가 자동으로 프로비저닝 된 AuthGuard를 사용
  @UseGuards(AuthGuard('local'))
  @Post('auth/login')
  async login(@Request() req) {
    return req.user;
  }
}

토큰 생성만 테스트

AuthGuard를 별도의 클래스로 만들자

auth/local-auth.guard.ts 생성

import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}

app.controller 수정

import { Controller, Request, Post, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

// LocalAuthGuard 추가
import { LocalAuthGuard } from './auth/local-auth.guard';

@Controller()
export class AppController {

  // LocalAuthGuard 로 바뀜 ㅋ
  @UseGuards(LocalAuthGuard)
  @Post('auth/login')
  async login(@Request() req) {
    return req.user;
  }
}

JWT 발급을 auth.service로 옮기자.

auth.service.ts 수정


import { Injectable } from '@nestjs/common';
import { UsersService } from '../users/users.service';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
  constructor(
    private usersService: UsersService,
    private jwtService: JwtService
  ) {}

  async validateUser(username: string, pass: string): Promise<any> {
    const user = await this.usersService.findOne(username);
    if (user && user.password === pass) {
      const { password, ...result } = user;
      return result;
    }
    return null;
  }

  // 로그인 기능이 추가되었다.
  async login(user: any) {
    const payload = { username: user.username, sub: user.userId };
    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}

상수 모음 생성 - auth/constants.ts

이를 사용하여 JWT 서명 및 확인 단계간에 키를 공유함

export const jwtConstants = {
  secret: 'secretKey',
};

auth.module.ts 수정

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from './constants';

@Module({
  imports: [
    UsersModule,
    PassportModule,
    
    // JwtModule 추가
    JwtModule.register({
      secret: jwtConstants.secret,
      signOptions: { expiresIn: '60s' },
    }),
  ],
  providers: [AuthService, LocalStrategy],
  exports: [AuthService],
})
export class AuthModule {}

app.controller.ts 수정

auth.service를 이용하여 로그인하는 부분이 추가됨.

import { Controller, Request, Post, UseGuards } from '@nestjs/common';
import { LocalAuthGuard } from './auth/local-auth.guard';
import { AuthService } from './auth/auth.service';

@Controller()
export class AppController {
  constructor(private authService: AuthService) {}

  @UseGuards(LocalAuthGuard)
  @Post('auth/login')
  async login(@Request() req) {
    return this.authService.login(req.user);
  }
}

JWT로 요청하는 기능 구현

auth/jwt.strategy.ts

import { ExtractJwt, Strategy } from 'passport-jwt';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable } from '@nestjs/common';
import { jwtConstants } from './constants';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: jwtConstants.secret,
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}

auth.module.ts 수정

프로바이더에 JwtStrategy가 추가되었다. JWT 생성시와 동일한 비밀키를 사용한다. 서명의 유지 시간은 60초로 하자.

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LocalStrategy } from './local.strategy';
import { JwtStrategy } from './jwt.strategy';
import { UsersModule } from '../users/users.module';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from './constants';

@Module({
  imports: [
    UsersModule,
    PassportModule,
    JwtModule.register({
      secret: jwtConstants.secret,
      signOptions: { expiresIn: '60s' },
    }),
  ],
  providers: [AuthService, LocalStrategy, JwtStrategy],
  exports: [AuthService],
})
export class AuthModule {}

AuthGuard를 확장하는 JwtAuthGuard 정의

auth/jwt-auth.guard.ts 생성

import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}

Jwt 인증 부분 추가

app.controller.ts 수정


import { Controller, Get, Request, Post, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from './auth/jwt-auth.guard';
import { LocalAuthGuard } from './auth/local-auth.guard';
import { AuthService } from './auth/auth.service';

@Controller()
export class AppController {
  constructor(private authService: AuthService) {}

  @UseGuards(LocalAuthGuard)
  @Post('auth/login')
  async login(@Request() req) {
    return this.authService.login(req.user);
  }

  // 방금 만든 따끈따끈한 AuthGuard
  @UseGuards(JwtAuthGuard)
  @Get('profile')
  getProfile(@Request() req) {
    return req.user;
  }
}

curl 을 이용한 로그인/인증

후... 일단 한번 굴려 봤네... 이제 이걸로 인증 쌉가능 ㄱㅅ

0개의 댓글