[데브캠프]nest.js 공식문서 -보안

또또·2024년 5월 20일

nest.js 공식문서

목록 보기
2/6
post-thumbnail

인증

서버는 인증을 증명하기 위해 후속요청시 인증헤더에 전달자 토큰으로 전송될 수 있는 jwt 발행
또한 유효한 jwt가 포함된 요청에만 엑세스 할 수 있는 보호된 경로 생성

인증모듈 생성

authmodule,AuthService, AuthController을 실행하는 것부터 시작

$ nest g module auth
$ nest g controller auth
$ nest g service auth

로그인 끝점 구현

AuthService에서 사용자를 검색하고 비밀번호를 확인하는 작업을 진행
서비스파일에 로그인 하기 위한 함수를 만든다

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

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

  async signIn(username: string, pass: string): Promise<any> {
    const user = await this.usersService.findOne(username);
    if (user?.password !== pass) {
      throw new UnauthorizedException();
    }
    const { password, ...result } = user;
    // TODO: Generate a JWT and return it here
    // instead of the user object
    return result;
  }
}

여기에 있는 유저 서비스에서 한명 검색하는걸 가져와

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

검색하고 확인

jwt

인증시스템을 jwt 사용

npm install --save @nestjs/jwt

jwt를 발행하는 코드를 서비스계층에 추가한다


import { Injectable, UnauthorizedException } 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 signIn(
    username: string,
    pass: string,
  ): Promise<{ access_token: string }> {
    const user = await this.usersService.findOne(username);
    if (user?.password !== pass) {
      throw new UnauthorizedException();
    }
    const payload = { sub: user.userId, username: user.username };
    return {
      access_token: await this.jwtService.signAsync(payload),
    };
  }
}

맨 밑에 jwt를 생성하는 함수를 사용
auth폴더에 contants.ts을 만들어 jwt설정을 위한 상수정의

export const jwtConstants = {
  secret: 'yourSecretKey',  // 여기에 당신의 비밀 키를 입력하세요
};

dlgn authmodule을 업데이트 해 필요한 의존성을 가져오고 jwtmodule 설정

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

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

jwt 검증

jwt검증하기 위해 검증 미들웨어 구현

import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
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.guards.ts를 만든 다음


import {
  CanActivate,
  ExecutionContext,
  Injectable,
  UnauthorizedException,
} from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { jwtConstants } from './constants';
import { Request } from 'express';

@Injectable()
export class AuthGuard implements CanActivate {
  constructor(private jwtService: JwtService) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const request = context.switchToHttp().getRequest();
    const token = this.extractTokenFromHeader(request);
    if (!token) {
      throw new UnauthorizedException();
    }
    try {
      const payload = await this.jwtService.verifyAsync(
        token,
        {
          secret: jwtConstants.secret
        }
      );
      // 💡 We're assigning the payload to the request object here
      // so that we can access it in our route handlers
      request['user'] = payload;
    } catch {
      throw new UnauthorizedException();
    }
    return true;
  }

  private extractTokenFromHeader(request: Request): string | undefined {
    const [type, token] = request.headers.authorization?.split(' ') ?? [];
    return type === 'Bearer' ? token : undefined;
  }
}

보호하고 싶은 컨트롤러에 가서 적용한다

@UseGuards(AuthGuard)
  @Get('profile')
  getProfile(@Request() req) {
    return req.user;
profile
내가 바라던 곳이야 흔들리지 않게 맘을 잡아

0개의 댓글