gengminy
갱미니의 코딩일지
gengminy
전체 방문자
오늘
어제
  • 분류 전체보기 (61)
    • 🚀 프로젝트 (16)
      • 🎸 고스락 티켓 (4)
      • 🌞 내친소 (5)
      • 🥁 두둥 (7)
    • 📡 백엔드 (31)
      • 🌱 Spring Boot (13)
      • 🐱 Nest.js (10)
      • ⭐ Node.js (8)
    • 🏭 Infra (11)
      • ⚙ 준비를 위한 준비 (2)
      • 🥑 AWS (3)
      • 🐳 Docker (3)
      • ⚡ Github Actions (3)
    • 🌊 프론트엔드 (1)
      • 🌌 React.js (1)
    • 😎 주저리 (2)

블로그 메뉴

  • 💻Github
  • 📸Instagram
  • ✨Blog

공지사항

인기 글

태그

  • 네스트
  • 스프링부트
  • JWT
  • nodejs
  • nest
  • 스프링
  • oauth2
  • springboot
  • Spring
  • JSON
  • nestjs
  • GithubActions
  • github
  • OAuth
  • 도커
  • 깃헙액션
  • AWS
  • 슬랙알림
  • SlackAPI
  • docker

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
gengminy

갱미니의 코딩일지

[NestJs] 따라하면서 배우는 NestJs - 7 (JWT, passport 이용한 인증 구현)
📡 백엔드/🐱 Nest.js

[NestJs] 따라하면서 배우는 NestJs - 7 (JWT, passport 이용한 인증 구현)

2022. 7. 8. 14:48

✅ JWT, passport 모듈 추가

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

✔ @nestjs/jwt - nest에서 jwt를 사용하기 위한 모듈

✔ @nestjs/passport - nest에서 passport를 사용하기 위한 모듈

✔ passport - passport 모듈

✔ passport-jwt - passport와 jwt를 연동하기 위한 모듈

 

 

📝./src/auth/auth.module.ts

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { UserRepository } from './user.repository';

@Module({
    imports: [
        PassportModule.register({ defaultStrategy: 'jwt' }),
        JwtModule.register({
            secret: 'MySecretKey1234',
            signOptions: {
                expiresIn: 3600,
            },
        }),
        TypeOrmModule.forFeature([UserRepository]),
    ],
    controllers: [AuthController],
    providers: [AuthService],
})
export class AuthModule {}

passport는 PassportModule.register로 등록

✔ defaultStrategy - 전략 지정, 여기서는 jwt를 이용한 로그인이니까 jwt로 지정

 

jwt는 JwtModule.register 로 등록하면 됨

✔ secret - secret key를 지정한다

✔ signOptions.expiresIn - 토큰 만료 기간을 지정, 3600은 한 시간

 

 

📝./src/auth/auth.service.ts

import { Injectable, UnauthorizedException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { AuthCredentialDto } from './dto/auth-credential.dto';
import { UserRepository } from './user.repository';
import * as bcrypt from 'bcryptjs';
import { JwtService } from '@nestjs/jwt';

@Injectable()
export class AuthService {
    constructor(
        @InjectRepository(UserRepository)
        private userRepository: UserRepository,
        private jwtService: JwtService,
    ) {}

    async signUp(authCredentialDto: AuthCredentialDto): Promise<void> {
        return this.userRepository.createUser(authCredentialDto);
    }

    async signIn(
        authCredentialDto: AuthCredentialDto,
    ): Promise<{ accessToken: string }> {
        const { username, password } = authCredentialDto;
        const user = await this.userRepository.findOne({ username });

        if (user && (await bcrypt.compare(password, user.password))) {
            //유저 토큰 생성 (Secret + Payload 필요)
            const payload = { username };
            const accessToken = await this.jwtService.sign(payload);

            return { accessToken };
        } else {
            throw new UnauthorizedException('Login Failed');
        }
    }
}

JwtService에 대한 의존성 주입 이후

리턴 값으로 accessToken을 객체로 감싸서 반환

 

여기서 payload 에 간단한 정보를 담아서 서명할 수 있다

(중요한 정보는 절대 담지 않도록 주의)

 

 

post 요청 보내면 엑세스 토큰이 날라온다

 

 

✅ 토큰 인증 기능 구현

npm i @types/passport-jwt --save

타입스크립트에서 passport, jwt 를 위한 타입을 사용하기 위한 모듈

 

 

📝./src/auth/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 { User } from './user.entity';
import { UserRepository } from './user.repository';

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
    constructor(
        @InjectRepository(UserRepository)
        private userRepository: UserRepository,
    ) {
        super({
            secretOrKey: 'MySecretKey1234',
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
        });
    }

    async validate(payload) {
        const { username } = payload;
        const user: User = await this.userRepository.findOne({ username });

        if (!user) {
            throw new UnauthorizedException();
        }

        return user;
    }
}

JWT Strategy 는 PassportStrategy 를 상속받아서 구현한다

생성자에서는 DB와의 연동을 위해 userRepository 주입

또한 부모인 PassportStrategy 의 생성자를 통해 비밀 키와 토큰 타입을 지정해준다

아까 생성할때 사용한 비밀키와 똑같이 넣고

여기서는 BearerToken 을 사용하기 떄문에 그렇게 주입

 

 

📝./src/auth/auth.module.ts

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';
import { JwtStrategy } from './jwt.strategy';
import { UserRepository } from './user.repository';

@Module({
    imports: [
        PassportModule.register({ defaultStrategy: 'jwt' }),
        JwtModule.register({
            secret: 'MySecretKey1234',
            signOptions: {
                expiresIn: 3600,
            },
        }),
        TypeOrmModule.forFeature([UserRepository]),
    ],
    controllers: [AuthController],
    providers: [AuthService, JwtStrategy],
    exports: [JwtStrategy, PassportModule],
})
export class AuthModule {}

auth module 외부에서도 사용할 수 있게 하기 위해서

JwtStrategy 와 PassportModule 을 export 해줌

provider 에 JwtStrategy 도 등록해준다

 

 

📝./src/auth/auth.controller.ts

@Post('/test')
@UseGuards(AuthGuard())
test(@Req() req) {
    console.log('req', req);
}

@UseGuards 를 통해 인증 처리를 해줄 수 있다

Pipe 와 마찬가지 처럼 Guard 도 미들웨어로서 동작한다

인증과 관련된 것이기 때문에 AuthGuard() 를 주입해준다

Guard를 주입하지 않으면 인증 처리가 제대로 동작하지 않음

 

 

✅ req.user 바로 가져오기 - 커스텀 데코레이터 생성

 

📝./src/auth/get-user.decorator.ts

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

export const GetUser = createParamDecorator((data, ctx: ExecutionContext): User => {
        const req = ctx.switchToHttp().getRequest();
        return req.user;
    },
);

createParamDecorator를 통해 파라미터 레벨의 데코레이터를 만들 수 있다

인자로 함수를 넣는데 ctx 부분을 ExecutionContext 로 지정해주면 된다

request를 받아서 반환하면 req.user를 즉시 반환 가능

 

⚠ 단, request 에 user가 있다는 전제 하에만 사용해야 한다

 

 

📝./src/auth/auth.controller.ts

@Post('/test')
@UseGuards(AuthGuard())
test(@GetUser() user: User) {
    console.log('user', user);
}

확실히 req.user 를 가져오는 것 보다 깔끔해지긴 한다

저작자표시 (새창열림)

'📡 백엔드 > 🐱 Nest.js' 카테고리의 다른 글

[NestJs] 따라하면서 배우는 NestJs - 9 (로그, 설정)  (0) 2022.07.09
[NestJs] 따라하면서 배우는 NestJs - 8 (권한 부여, 유저와 게시글 관계 부여)  (0) 2022.07.09
[NestJs] 따라하면서 배우는 NestJs - 6 (auth 모듈 구현)  (0) 2022.07.07
[NestJs] 따라하면서 배우는 NestJs - 5 (레포지토리 구현 및 DB 이용 CRUD)  (0) 2022.07.07
[NestJs] TypeORM 사용 시 RepositoryNotFoundError 해결하기  (0) 2022.07.06
    '📡 백엔드/🐱 Nest.js' 카테고리의 다른 글
    • [NestJs] 따라하면서 배우는 NestJs - 9 (로그, 설정)
    • [NestJs] 따라하면서 배우는 NestJs - 8 (권한 부여, 유저와 게시글 관계 부여)
    • [NestJs] 따라하면서 배우는 NestJs - 6 (auth 모듈 구현)
    • [NestJs] 따라하면서 배우는 NestJs - 5 (레포지토리 구현 및 DB 이용 CRUD)
    gengminy
    gengminy
    코딩

    티스토리툴바