새소식

🗂️ Project/🧸 Instagram-Clone

[Instagram-Clone] JWT 인증

  • -

GIthub : https://github.com/juuuuung/Instagram_clone

 

GitHub - juuuuung/Instagram_clone

Contribute to juuuuung/Instagram_clone development by creating an account on GitHub.

github.com

 


JWT?

내가 설명하면 말이 길어질 수 있으니 정말 좋은 글을 찾아 왔다.

 

[Server] JWT(Json Web Token)란?

현대 웹서비스에서는 토큰을 사용하여 사용자들의 인증 작업을 처리하는 것이 가장 좋은 방법이다. 이번에는 토큰 기반의 인증 시스템에서 주로 사용하는 JWT(Json Web Token)에 대해 알아보도록 하

mangkyu.tistory.com

jwt에 대해서는 망나니개발자님의 글을 참고 하면 좋을것 같다.

 

 


무엇을 어떻게 했는가

 

난 JWT와 SESSION 방식 중에 어떤걸 사용해야 할까 고민 했다.

결론적으로 JWT를 사용했으며, 그 이유는 간단하다 한번도 해본적이 없어서 경험해 보고 싶었다.

 

각설하고 어떻게 구현했는지 코드를 보자

(솔직히 인터넷 뒤지면서 막 따라 했다)

 

JWT를 Nest에서 사용하기위해서는 passport를 설치 해야한다

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

 

그리고

nest g mo auth
nest g s auth

AuthModule과 AuthService를 만들어 준다.

( Nestjs 공식 문서에서는 UserModule과 UserService까지 한다 )

 

// auth/auth.service.ts

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { appRepository } from 'src/app.repository';

@Injectable()
export class AuthService {
  constructor(
    private readonly appRepository: appRepository,
    private readonly jwtService: JwtService,
  ) {}

  async validateUser(userId: string, pass: string): Promise<any> {
    const user = await this.appRepository.findUser(userId);

    if (user[0] && user[0].userPw === pass) {
      const { userPw, ...result } = user[0];
      return result;
    }
    return null;
  }

  async login(user: any) {
    const payload = {userId : user.userId, nickName : user.nickName, role : user.role} 

    return {
      access_token: this.jwtService.sign(payload),
    };
  }
}

나는 prisma를 사용하고 Repository를 만들어서 바로 연결하고 싶었기 때문에 appRepository를 주입 해주었다

validateUser Method는 뭘 하는 아이인가 물어본다면 인증 로직이다.

 

AuthModule을 수정하고 가자

// server/src/auth/auth.module.ts

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { appRepository } from 'src/app.repository';
import { JwtService } from '@nestjs/jwt';
import { PrismaService } from 'src/prisma.service';

@Module({
  providers: [
    AuthService,
    appRepository,
    PrismaService,
    JwtService,
  ],
  exports: [AuthService],
})
export class AuthModule {}

export : [AuthService] 를 해주어야 AppModule에서 AuthModule를 import하고 AppController에서 AuthService를 사용 할 수 있다

지금은 이해 하려 하지말고 그냥 그렇구나 하고 넘어가도 상관 없는 말이다 

 

이제 아까 인증 로직이 어디에 사용되는지 알아보려면 

새로운 폴더와 파일을 만들어야한다

auth/strategy 폴더 생성하기
auth/strategy/local.strategy.ts 파일 생성
// server/src/auth/strategy/local.strategy.ts

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

@Injectable()
export class LocalStrtegy extends PassportStrategy(Strategy) {
  constructor(private readonly authService: AuthService) {
    super({ usernameField: 'userId', passwordField: 'userPw' });
  }

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

validate함수의 기본 인자는 (username, password) 이다 변경을 원하면 super에서 바꿔주면 된다

server/src/auth/guard 폴더 생성
server/src/auth/guard/local-auth.guard.ts 파일 생성
// server/src/auth/guard/local-auth.guard.ts

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

@Injectable()
export class LocalAuthGuard extends AuthGuard('local') {}
// server/src/auth/auth.module.ts

import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { JwtModule, JwtService } from '@nestjs/jwt';
import { appRepository } from 'src/app.repository';
import { PrismaService } from 'src/prisma.service';

@Module({
  providers: [
    AuthService,
    LocalStrategy,
    appRepository,
    PrismaService,
    JwtService
  ],
  exports: [AuthService],
})
export class AuthModule {}

 

JWT
$ npm install --save @nestjs/jwt passport-jwt
$ npm install --save-dev @types/passport-jwt
import { ExecutionContext, HttpException, Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { AuthGuard } from '@nestjs/passport';
import { jwtConstants } from '../constants';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  constructor(private readonly jwtService: JwtService) {
    super();
  }
  canActivate(context: ExecutionContext): boolean {
    const req = context.switchToHttp().getRequest();
    const { authorization } = req.headers;
    if (authorization === undefined) {
      throw new HttpException('not good', 401);
    }
    req.user = this.validateToken(authorization.replace('Bearer ', ''));
    return true;
  }

  validateToken(token: string) {
    try {
      return this.jwtService.verify(token, jwtConstants);
    } catch (error) {
      const errorMsgArray = [
        'EXPIRED_TOKEN',
        'INVALID_TOKEN',
        'TOKEN_IS_ARRAY',
        'NO_USER',
      ];

      switch (error.message) {
        case errorMsgArray.includes(error.message, 1):
          throw new HttpException('유효하지 않은 토큰 입니다.', 401);
        case errorMsgArray.includes(error.message):
          throw new HttpException('토큰이 만료되었습니다.', 401);
        default:
          throw new HttpException('서버 오류입니다.', 401);
      }
    }
  }
}

auth Module에 추가 해줘야 할것이 있다.

// server/src/auth/auth.module.ts

import { Module } from '@nestjs/common';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { LocalStrtegy } from './strategy/local.strategy';
import { JwtModule } from '@nestjs/jwt';
import { jwtConstants } from './constants';
import { JwtStrategy } from './strategy/jwt.strategy';
import { appRepository } from 'src/app.repository';
import { PrismaService } from 'src/prisma.service';

@Module({
  imports: [
    PassportModule,
    JwtModule.register({
      secret: jwtConstants.secret,
      signOptions: { expiresIn: '1m' },
    }),
  ],
  providers: [
    AuthService,
    LocalStrtegy,
    JwtStrategy,
    appRepository,
    PrismaService,
  ],
  exports: [AuthService],
})
export class AuthModule {}

 

자이제 app.controller.ts에 추가해보자

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

@Controller('auth')
export class AppController {
  constructor(private readonly authService: AuthService) {}
  @UseGuards(LocalAuthGuard)
  @Post('login')
  async login(@Request() req) {
    return this.authService.login(req.user);
  }
}

localAuthGuard -> localStrategy.validate -> authService.validateUser 성공시 req에 user주입 -> authService.login(req.user)

 

api key

로그인에 성공하면 payload를 넣은 apikey를 준다

 

 

 

 

 


글을 쓴 후

 

솔직하게 jwt를 어떻게 사용할지 몰라 많이 당황스러웠으며, 다른사람의 코드를 따라한것이다.

이제 nestJs 어떤 방식으로 돌아가는지 발톱만큼은 이해한것 같다

 

다음글은 role Gaurd에 대해 써볼 생각이다.

 

 

 

 

 

 

 

 

 

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.