Nest.js (6) JWT & custom decorator

Seong·2022년 11월 22일
0

Nest.js

목록 보기
6/9
post-thumbnail

JWT(JSON Web Token)란?

당사자간에 정보를 JSON 개체로 안전하게 전송하기위한 컴팩트하고 독립적인 방식을 정의하는 개방적 표준

JWT의 구조

header :토큰에 대한 메타 데이터(타입,해싱알고리즘,SHA256,RSA 등등등)
payload :유저정보(issuser),만료 기간(expiration time),주제(subject) 등등
Verify Signature : 서명

JWT생성하기

필요한 모듈들을 설치하기

npm i @nestjs/jwt @nestjs/passport passport passport-jwt

그후 module에 jwt모듈을 추가한다.

    PassportModule.register({ defaultStrategy: 'jwt' }),
    JwtModule.register({
      secret:process.env.JWTSECRET,
, 
      signOptions: {
        expiresIn: 60 * 60, //유효시간
      },
    })

서비스에 주입한다.

constructor(
   @InjectRepository(User)
   private userRepository: Repository<User>,
   private jwtService: JwtService,
 ) 

그 후 로그인시에 토큰을 생성한다

 async userLogin(userDTO: UserDTO): Promise<{ accessToken: string }> {
   const { userName, password } = userDTO;
   const user = await this.userRepository.findOneBy({ userName });
   if (user && (await bcrypt.compare(password, user.password))) {
     //유저 토큰 생성 (Secret + Payload)
     const payload = { userName }; //payload에는 중요한 정보를 넣지 않는다.
     const accessToken = await this.jwtService.sign(payload); //알아서 시크릿과 pay로드는 합쳐서 accessToken을 생성해줌
     return { accessToken };
   } else {
     throw new UnauthorizedException('login failed');
   }
 }

JWT로 인증하기

npm i -D @types/passport-jwt   //로 타입정의

jwt.guard.ts 파일 작성

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

@Injectable()
export class JwtAuthGurad extends AuthGuard('jwt') {
  //스트레터지를 자동으로 실행
}

jwt.strategy.ts 파일 작성

import { Injectable, UnauthorizedException } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { InjectRepository } from '@nestjs/typeorm';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { Repository } from 'typeorm';
import { User } from '../entity/user.entity';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
  ) {
    super({
      secretOrKey:process.env.JWTSECRET, //시크릿키가 유효한지 체크
      jwtFromRequest:   ExtractJwt.fromAuthHeaderAsBearerToken(),  //이거 대소문자 구분잘해야함
    });
  }

  async validate(payload) {
    //토큰이 유효할시 실행됨
    const { userName } = payload;
    const user: User = await this.userRepository.findOne({
      where: {
        userName,
      },
      select:  ['id','userName'], //id랑 userName만 받아오기
      
    });
    if (!user) {
      throw new UnauthorizedException();
    }
    return user;
  }
}

작성한 JwtStrategy를 AuthModule Provider 항목에 넣어주고
다른곳에서도 사용해야하기때문에 export도 해준다

  providers: [AuthService,JwtStrategy],
  exports: [JwtStrategy,PassportModule]

그후 UseGuard()를 이용해서 req.user를 받아오면 된다.

@Post('/login/test')
@UseGuards(JwtAuthGuard)
test(@Req() req){
 return req.user;
}

인증
인증은 크게 2가지로 나눠진다. 세션 기반 인증과 토큰 기반 인증
세션
세션 기반 인증 : 로그인시 서버는 따로 생성해둔 세션DB에 해당 사용자를 저장시키고 이후 사용자로부터 요청이 올 때마다 세션 DB를 체크해 저장되어있는지 확인한다.
세션 인증 장점 : DB에 계속 저장중이므로 특정 사용자를 벤하거나 로그인된 다른 기기에서 다른기기의 로그아웃 등의 작업이 가능하다.
세션 인증 단점 : 요청마다 DB에 체크해야하므로 느리다(그래서 Redis를 주로 이용함). 세션 DB를 따로 두어야함. DB에 저장해야하므로 특정 길이 제한을 둔다.
토큰
토큰 기반 인증 : 로그인시 서버는 토큰을 생성해 보내주고 토큰을 따로 DB에 저장하지 않는다. 이후 사용자로부터 요청이 올 때마다 토큰에 대한 검증만 수행한다. JWT 를 주로 이용한다.
토큰 인증 장점 : 빠르다. Facebook, Google 등 계정으로 다른 서비스 로그인이 가능한 OAuth 구현이 가능하다. 따로 DB를 둘 필요가 없다.
토큰 인증 단점 : 토큰 자체가 정보이므로 탈취시 취약하다. ex) 토큰 유효기간 30분이라면 해당 사용자가 악의적인 사용자여도 내가 해당 사용자를 30분동안 벤하지 못함.

커스텀 데코레이터 생성하기

import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { User } from '../entity/user.entity';

export const getUser = createParamDecorator((data, ctx: ExecutionContext):User => {
  const req = ctx.switchToHttp().getRequest();
  return req.user;
});
profile
메모장

0개의 댓글