Search

#031 #JWT / 로그인

 JWT에 대한 참고 블로그

 로그인 비즈니스 처리 과정

→ front의 요청으로 LoginAPI에 도달하고 검증을 거쳐 secretKey로 JWT를 생성, 그리고 그것을 반환. → 앞으로는 매 요청시마다 JWT와 함께 요청하면 된다. 반환된 JWT는 안전한 곳에 보관해야 된다.

 JWT 인증 비즈니스 처리 과정

→ FRONT는 매 요청시 마다 JWT를 같이 보내서 검증을 받아야된다. → 이 JWT는 Guard를 거치고, Strategy를 거쳐서 디코딩을 하게 되고, req에 정보가 담기게 된다. → 이 req를 가지고 API를 처리하고 반환

 요청의 생명주기

 passport

 설치

npm install --save @nestjs/passport passport passport-local npm install --save-dev @types/passport-local
JavaScript
복사
npm install --save @nestjs/jwt passport-jwt npm install --save-dev @types/pass port-jwt
JavaScript
복사

 auth 모듈 생성

1.
설치
nest g module auth
JavaScript
복사
2. 확인
auth 폴더랑 auth.module.ts 가 생성되었다.

 auth 서비스 생성

1.
설치
nest g service auth
JavaScript
복사
2.
확인
auth.service.ts 파일이 생성이 되었다.
3.
module 연결
명령어로 AuthModule을 생성하게 되면, AppModule/AuthModule에 자동으로 연결해서 추가해준다.

 jwt.guard.ts

 파일 생성

auth > jwt 폴더에서 jwt.guard.ts 를 생성한다 .

 jwt.guard.ts 정의

import { Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; @Injectable() export class JwtAuthGuard extends AuthGuard('jwt') {}
JavaScript
복사
→ 이 클래스의 특징을 보면 이것 또한 의존성 주입이 가능한 클래스라는 것을 알 수 있다. → AuthGuardstrategy를 자동으로 실행해주는 기능이 있다.

 jwt.strategy.ts

jwt의 전략, 쉽게말해 상세설정을 하는 파일이라고 생각하면 된다.

 정의

import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy } from 'passport-jwt'; @Injectable() export class JwtStrategy extends PassportStrategy(Strategy) { constructor() { super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), secretOrKey: 'secretKey', ignoreExpiration: false, }); } async validate(payload) {} }
JavaScript
복사
jwtFromRequest : jwt를 어디서 꺼낼건지에 대한 설정, 우린 헤더 bearer토큰으로 꺼내겠다는 설정 → secretOrKey : 여기서는 어떻게 암호화 할건지에 대한 키워드 부분, 이건 환경변수로 숨겨두어야 된다. 이 것으로 디코딩을 하기 때문 → ignoreExpiration : 만료기간을 설정하는데, 만료기간일 길 수록 탈취당하면 다 털릴 위험이 커진다.

 auth.module.ts

이제 auth.module에서 필요한 모듈들을 import 해보자.

 정의

import { Module } from '@nestjs/common'; import { AuthService } from './auth.service'; import { PassportModule } from '@nestjs/passport'; import { JwtModule } from '@nestjs/jwt'; import { JwtStrategy } from './jwt/jwt.strategy'; @Module({ imports: [ PassportModule.register({ defaultStrategy: 'jwt', session: false }), JwtModule.register({ secret: 'secret', signOptions: { expiresIn: '1y' }, }), ], providers: [AuthService, JwtStrategy], }) export class AuthModule {}
JavaScript
복사
PassportModule : 여기서는 우리가 Strategy에 대해서 기본적인 설정을 할 수 있다. → JwtModule : 이따 로그인 할 때 사용할 모듈

 auth.service.ts

 의존성 주입 먼저

import { CatsRepository } from './../cats/cats.repository'; import { Injectable } from '@nestjs/common'; @Injectable() export class AuthService { constructor(private readonly catsRepository: CatsRepository) {} }
JavaScript
복사
AuthService에 catsRepository를 의존성 주입을 한다.

 module 등록

→ AuthModule에 위 처럼 CatsModule을 등록해주면 Cats 도메인에 export된 모듈들을 가져와서 사용할 수 잇다. Repository를 가져오기 위해서 위의 기능을 구현했다. → 일단 nestJS는 공급자가 기본적으로 캡슐화가 되어있기 때문에, 여기서 Repository를 가져와서 쓸수 있게 하려면, CatsModule에서 캡슐화된 요소를 export해야된다.
이런 식으로 exports에 Repository를 등록하면 가져다가 다른 모듈에서 사용할 수 있다.

 AuthService

 jwtLogin 로직 구현

import { CatsRepository } from './../cats/cats.repository'; import { Injectable } from '@nestjs/common'; @Injectable() export class AuthService { constructor(private readonly catsRepository: CatsRepository) {} async jwtLogin(data: LoginRequestDto) { const { email, passworď } = data; } }
JavaScript
복사
일단 기본 구성 한 것이고, LoginRequestDto를 만들어 줘야 된다.

 login.request.dto.ts

1.
파일 생성
auth > dto 폴더 내부에 파일을 생성한다.
2.
코드 구현
import { PickType } from '@nestjs/swagger'; import { Cat } from 'src/cats/cats.schema'; export class LoginRequestDto extends PickType(Cat, [ 'email', 'password', ] as const) {}
JavaScript
복사
모든 설정을 스키마에 작성을 해두었으니, 이렇게 PickType으로 가져와서 쓰면 되니까 간단하게 구현 되었다.

 email 중복 체크

repository에 고양이 정보를 체크하는 findCatByEmail메소드를 구현해 줄 것이다.

 catsRepository.findCatByEmail 구현

위와 같이 구현하면 되는데, findOne이랑 exists 메소드의 차이점에 대해서 궁금할 수 있다. findOne : email로 찾은 객체를 반환한다. exists : email로 해당 객체의 존재여부만 반환한다. (데이터 없음)

 password 일치 여부 체크

bcypt 내부에 compare라는 메소드가 있는데, 여기서 받아온 password와 저장된 cat.password를 비교한다.

 JWT 생성 하기

1.
의존성 주입하기
JWT를 생성하기 위해서는 JwtService의 의존성 주입이 필요하다.
2.
JWT 토큰 생성
JWT 만드는 과정에서 개발자의 역할은 payload를 만들어 주는 역할이다. payload는 다음에 로직을 구현할 때 자주 쓸거 같은 최소한의 필수 정보만 넣어야 된다. email과 id 정도의 정보만 넣어서 토큰을 만들자. (나는 사실 id만 있어도 충분하다고 생각함)
3.
CatsController에 AuthService 의존성 주입
AuthService를 사용할려면 의존성 주입을 해야되고, 이 기능을 사용하기 위해서는 module에다가도 등록을 해줘야 된다.
4.
cats.module 추가
CatsModule에 AuthModule을 등록해줘야 의존성 주입을 사용할 수 있다. 하지만, 현재 AuthModule에도 CatsModule이 등록되어 있다.
이런 구조에서는 순환참조가 되어서 스택오버플로우가 발생할 가능성이 높다.
5.
순환 참조 해결 ⇒ 모듈 전달 참조 사용
임포트 되고 있는 CatsModule을 forwardRef(()⇒ 모듈) 로 감싸주면 된다. 이것을 양쪽 방향에서 걸어줘야 된다.
이렇게 적용하면 순환모듈참조 문제가 해결이 된다.

 로그인 비즈니스 로직 바인딩

의존성 주입된 authService를 가져와 jwtLogin 메소드를 연결한다. 요청 데이터는 @Body 데코레이터를 사용해서 데이터를 전달한다.

 postman 테스트

로그인 성공하고 토큰 또한 잘 반환 되었다.

 JWT 검증

JWT 공식 사이트 접속
발급받은 토큰 검증
JWT를 생성할 때 넣었던 값 또한 잘 들어가있는 것을 확인 exp : 는 만료 기간
시크릿 키 일치 시키기
AuthModule에서는 JWT 서명을 secret이란 단어로 했는데, 디코딩하는 과정에 시크릿키가 일치하지 않으면 디코딩 되지 않는다. 그래서
jwt.strategy.ts 내부에 있는 secretOrKey에 밸류값을 암호화할 때 썻던 밸류값이랑 일치시켜야 된다.

 의존성 주입 단계

의존성 주입하는 단계가 생각보다 많아 헷갈려서 정리해놓음
1. 의존성 주입 CatsController에 AuthService 주입 2. CatsModule에 AuthService 등록 해당 도메인 Module에 AuthService가 imports에 등록이 되어야 함 3. imports가 될려면 exports 되어야 함 AuthService가 imports가 될려면 자체 모듈인 AuthModule에서 AuthService가 exports에 등록이 되어야함
JavaScript
복사
AuthModule을 다른 데서 imports할려면 이렇게 exports에 자기 자신을 등록