관리 메뉴

공부 기록장 💻

[NodeJS/NestJS] User 인증 Authentication - 로그인 기능 구현하기 / 비밀번호 암호화(password bycrpyt) 본문

# Tech Studies/NestJS

[NodeJS/NestJS] User 인증 Authentication - 로그인 기능 구현하기 / 비밀번호 암호화(password bycrpyt)

dream_for 2022. 8. 25. 04:06

 

이전 글에서 Nest JS 프레임워크 상에서

email(primary column)과 password(column) 두 필드를 이용해 user DTO를 생성하고,

회원가입 기능을 구현해 보았다.

 

이번에는 로그인 기능을 구현해보도록 하자.

 

 

Auth Service

 

auth.service.ts 에 다음과 같이 validateUser 함수를 추가로 작성하여 로그인하는 서비스를 등록하자.

먼저 userService의 findByFields 메소드가 email을 기준으로 데이터를 탐색하게 된다.

요청된 email과 동일한 email을 가진 데이터가 없거나, 탐색한 email을 가지느 유저의 password가 요청으로 들어온 password와 다른 경우 UnauthroizedException() 예외 처리를 하게 된다.

 

    // 로그인
    async validateUser(userDTO: UserDTO): Promise<UserDTO | undefined>{
        let userFind: UserDTO = await this.userService.findByFields({
            where: { email: userDTO.email}
        })
        // 사용자를 찾지 못한 경우와 비밀번호가 올바르지 않은 경우
        if(!userFind || userDTO.password != userFind.password ){
            throw new UnauthorizedException();
        }
        return userFind;
    }

 

 


Auth Controller

 

이번에는 auth.controller.ts 에 로그인하는 함수 signin을 만들자.

 

POST reqeust를 /sign-in 에 보내게 된다. (로컬 서버로 돌릴 때에는, localhost:3000/auth/sign-in)

Body 에는 userDTO 객체를 보내게 되며, authService의 validateUser 메서드를 실행하게 된다.

 

    // 로그인
    @Post('/sign-in')
    async signin(@Body() userDTO: UserDTO): Promise<any>{
        return await this.authService.validateUser(userDTO);
    }

 

 


Postman 실행 - password가 틀린 경우?

 

 

 

 

현재 모든 유저의 password는 모두 임의로 "12345"로 설정되어 있다.

 

다음과 같이 "54321"이라는 잘못된 password로 sign-in 시도를 한 경우,

statusCode 401과 함께 Unauthroized 오류 메시지가 함께 반환되는 것을 확인할 수 있다.

 

 

 


 

비밀번호 암호화: Bcrypt

 

회원 가입시 입력하는 비밀번호를 그대로 데이터베이스에 저장하여 사용할 경우, 보안 사고의 위험이 있다.

회원의 중요한 정보가 그대로 db에 저장되어 노출되는 것은 보안상 매우 위험한 일이다.

따라서 비밀번호와 같은 중요한 데이터는 반드시 암호화하여 저장해야 한다.

 

nodejs에서 제공하는 bcrypt 패키지를 사용하여 비밀번호를 암호화해보자.

 

 

 

다음 명령어를 사용하여 bcrypt, @types/bcrypt 패키지를 설치하자.

$ npm install --save bcrpyt @types/bcrypt

 

 


 

회원 가입 시 비밀번호 암호화

 

bcrpyt 패키지에 포함되어 있는 함수들 중, 비밀번호를 암호화하는 방법에는 두 가지가 있다고 한다. ((https://www.npmjs.com/package/bcrypt)

첫번째 방법은 genSalt() 함수를 이용하여 salt (해싱 함수에 쓰이는 랜덤 string) 와 hash를 각각 다른 함수 호출로 생성하는 방법이고,

두번째 방법은 salt의 길이만 지정하면 salt와 hash를 자동적으로 생성하는 함수이다. 

 

 

 

 

 

 

user.service.ts 파일을 수정해보자.

 

우선 bcrpyt 패키지의 모든 모듈을 bcrypt 이름으로 import 해주도록 하자.

이후 비밀번호를 암호화하는 transformPassword 함수를 작성해준다.

import * as bcrypt from 'bcrypt';

/*
......
*/


        // 비밀번호 암호화 (saltround를 10으로 지정)
        async transformPassword(user: UserDTO): Promise<void>{
            user.password = await bcrypt.hash(
                user.password, 10,
            );
            return Promise.resolve();
        }

 

 

그리고 새로운 유저를 등록할 때 사용되는 save() 함수를 다음과 같이 변경해준다.

userDto를 그대로 저장하기 전, transformPassword 메소드를 먼저 실행시켜 비밀번호를 암호화한 후에 저장하도록 한다.

        // 신규 유저 등록
        async save(userDTO: UserDTO): Promise<UserDTO | undefined>{
            await this.transformPassword(userDTO);
            console.log(userDTO);
            return await this.userRepository.save(userDTO);
        }

 

 


Postman 실행

 

이전과 동일하게 "12345" 를 비밀번호로 지정하여 post 요청을 보냈더니,

결과 값의 password에는 "12345"가 아닌 암호화된 string 이 저장된 것을 확인할 수 있다.

 

mysql db에도 암호화된 값이 저장된 것을 확인하였다.

 

 

 

 


비밀번호 검증

 

auth.service.ts 에서 로그인 함수를 변경해보자.

 

이번엔 bcrypt 패키지의 compare 메소드를 이용해 요청받은 userDTO의 비밀번호와, 

db 테이블에서 찾은 유저의 password를 비교하여 그 결과값을 validatePassword에 저장하도록 하자.

이 값이 false인 경우 비밀번호가 올바르지 않음을 의미한다.

 

import * as bcrypt from 'bcrypt';

/*
.....
*/


    // 로그인
    async validateUser(userDTO: UserDTO): Promise<UserDTO | undefined>{
        let userFind: UserDTO = await this.userService.findByFields({
            where: { email: userDTO.email}
        })
        const validatePassword = await bcrypt.compare(userDTO.password, userFind.password);

        // 사용자를 찾지 못한 경우와 비밀번호가 올바르지 않은 경우
        if(!userFind || !validatePassword ){
            throw new UnauthorizedException('Login Failed!');
        }
        return userFind;
    }
}

 

 

Postman을 통한 확인

 

올바르지 않은 password로 로그인한 경우, 401 Unauthorized 코드가 실행된다.

 

 

 

 

 

Promise 결과 반환 타입을 string 으로 바꾸고 리턴 값을 "login success"로 수정해보자.

 

    // 로그인
    async validateUser(userDTO: UserDTO): Promise<string | undefined>{
        let userFind: UserDTO = await this.userService.findByFields({
            where: { email: userDTO.email}
        })
        const validatePassword = await bcrypt.compare(userDTO.password, userFind.password);

        // 사용자를 찾지 못한 경우와 비밀번호가 올바르지 않은 경우
        if(!userFind || !validatePassword ){
            throw new UnauthorizedException('Login Failed!');
        }
        return "login success";
    }

 

올바른 비밀번호를 입력하게 되면,

 

이젠 userDTO가 아닌, login success 문자열이 반환된 것을 확인할 수 있다.


[참고자료]

https://www.youtube.com/watch?v=LbyfF6ALMo0&list=PLeNJ9AVv90q1kEX-bz1r_56imtp5_aJ6q&index=15 

 

728x90
반응형
Comments