오늘도 개발

Node.js에서 인증, 인가 진행하기(bcrypt, jwt) 본문

웹 프로그래밍/Javascript

Node.js에서 인증, 인가 진행하기(bcrypt, jwt)

Sueeeeeee 2022. 9. 29. 15:18

1. 인증(회원가입)

1) req.body의 key 확인

js는 파이썬처럼 keyError가 없음. (없는 키 호출 시 undefined 반환)

직접 key가 있는지 없는지 확인해야 함.

 

Object.entries(딕셔너리)

딕셔너리의 키, 값을 리스트로 바꿔줌

const info = { username: 'garfield', password: '123' }
Object.entries(info) // [ [ 'username', 'garfield' ], [ 'password', '123' ] ]

배열.forEach(콜백함수)

배열을 iterate하며 콜백함수에 각 요소를 인자로 넣어 호출함

map과 달리 반환값이 없음.

const arr = [1, 2]
arr.forEach((val) => {
   console.log(val * 10)
})

// 10
// 20

배열.includes(값)

배열에 어떤 값이 있는지 없는지 확인해서 true, false 반환

const arr = [1, 2, 3]
console.log(arr.includes(1)) // true

정리

const { username, password } = req.body; // { username: 'garfield', password: '123' }

    // 1) 키값 체크 - hasKey 딕셔너리 채우기
    const hasKey = { password: false, username: false };
    const requireKey = Object.keys(hasKey); // ['password', 'username']
    Object.entries(req.body).forEach((keyValue) => { // [ [ 'username', 'garfield' ], [ 'password', '123' ] ]
        const [key, value] = keyValue; 
        if (requireKey.includes(key) && value){
            hasKey[key] = true;
        }
    })

    // 2) 키값 체크 - hasKey iterate하면서 값 없으면 400에러 반환하기
    const hasKeyArray = Object.entries(hasKey); // [ [ 'password', 'true' ], [ 'username', 'true' ] ]
    for (let i = 0; i < hasKeyArray.length; i++){
        const [key, value] = hasKeyArray[i];
        if (!value){
            res.status(400).json({message: `${key}값이 없습니다`});
            return;
        }
    }

2) bcrypt로 비밀번호 암호화하기

1. bcrypt 설치

npm install bcrypt

2. 코드 작성

async 함수 내에서 사용할 때는 genSalt, hash 메서드 대신 genSaltSync, hashSync 메서드 사용 

// 1. 모듈 불러오기
const bcrypt = require('bcrypt');

const { username, password } = req.body; // { username: 'garfield', password: '123' }

// 2. 솔트값 생성
const salt = bcrypt.genSaltSync(12); 
// 12는 솔트 횟수. 값 높을수록 보안 좋아지나 속도 저하
// $2a$12$B9cOW5a3MbEoqaiIRsXvjO

// 3. 해시 함수에 넣기
const hashedpw = bcrypt.hashSync(password, salt) // $2a$12$B9cOW5a3MbEoqaiIRsXvjOZTacL4uD6SCKjY1PpuAq/O0CGLLSLZe

3) 정리

app.post('/signup', async (req, res) => {
    const { username, password } = req.body; // { username: 'garfield', password: '123' }

    // 1) 키값 체크 - hasKey 딕셔너리 채우기
    const hasKey = { password: false, username: false };
    const requireKey = Object.keys(hasKey); // ['password', 'username']
    Object.entries(req.body).forEach((keyValue) => { // [ [ 'username', 'garfield' ], [ 'password', '123' ] ]
        const [key, value] = keyValue; 
        if (requireKey.includes(key) && value){
            hasKey[key] = true;
        }
    })

    // 2) 키값 체크 - hasKey iterate하면서 값 없으면 400에러 반환하기
    const hasKeyArray = Object.entries(hasKey); // [ [ 'password', 'true' ], [ 'username', 'true' ] ]
    for (let i = 0; i < hasKeyArray.length; i++){
        const [key, value] = hasKeyArray[i];
        if (!value){
            res.status(400).json({message: `${key}값이 없습니다`});
            return;
        }
    }

    // 3) 비밀번호 암호화
    const salt = bcrypt.genSaltSync(12); 
    const hashedPw = bcrypt.hashSync(password, salt) 

    try {
        // 4) DB에 저장
        await myDataSource.query(`
            INSERT INTO users(username, password)
            VALUES(?, ?)
        `, [username, hashedPw]);
        res.status(201).json({message: "userCreated"})
    }
    catch(err){
        console.log(err)
        res.status(500).json({message: "error"})
    }
});

 

2. 인가(로그인)

1) req.body의 key 확인

회원가입과 마찬가지로 키 확인

const { username, password } = req.body; // { username: 'garfield', password: '123' }

const hasKey = { password: false, username: false };
const requireKey = Object.keys(hasKey); // ['password', 'username']
Object.entries(req.body).forEach((keyValue) => { // [ [ 'username', 'garfield' ], [ 'password', '123' ] ]
    const [key, value] = keyValue; 
    if (requireKey.includes(key) && value){
        hasKey[key] = true;
    }
})

const hasKeyArray = Object.entries(hasKey); // [ [ 'password', 'true' ], [ 'username', 'true' ] ]
for (let i = 0; i < hasKeyArray.length; i++){
    const [key, value] = hasKeyArray[i];
    if (!value){
        res.status(400).json({message: `${key}값이 없습니다`});
        return;
    }
}

2) db에서 대조할 유저 정보 가져오기

const [user] = await myDataSource.query(`
    SELECT id, username, password 
    FROM users 
    WHERE username = ?
  `, [username])

3) (유저가 있다면) 비밀번호 맞는지 확인

if (!user) {
    console.log("NO_USER")
    res.status(400).json({message: "NO_USER"})
  }

  // bcrypt로 비번 일치하는지 확인
  const isPasswordCorrect = bcrypt.compareSync(password, user.password) // true or false

  if (!isPasswordCorrect) {
    res.status(400).json({message: "INVALID_PASSWORD"})
  }

4) jwt 토큰 발급

1) jwt 설치

npm install jsonwebtoken

2) 코드 작성 

// 최상단에서 모듈 임포트
const jwt = require('jsonwebtoken')

const token = jwt.sign({ userId: user.id }, 'secretKey');
res.status(200).json({message: 'LOGIN_SUCCESS', token: token})

5) 정리

const jwt = require('jsonwebtoken')

app.post('/login', async(req, res) => {
    const { username, password } = req.body; // { username: 'garfield', password: '123' }
    const hasKey = { password: false, username: false };
    const requireKey = Object.keys(hasKey); // ['password', 'username']
    Object.entries(req.body).forEach((keyValue) => { // [ [ 'username', 'garfield' ], [ 'password', '123' ] ]
        const [key, value] = keyValue; 
        if (requireKey.includes(key) && value){
            hasKey[key] = true;
        }
    })

    const hasKeyArray = Object.entries(hasKey); // [ [ 'password', 'true' ], [ 'username', 'true' ] ]
    for (let i = 0; i < hasKeyArray.length; i++){
        const [key, value] = hasKeyArray[i];
        if (!value){
            res.status(400).json({message: `${key}값이 없습니다`});
            return;
        }
    }
    const [user] = await myDataSource.query(`
        SELECT id, username, password 
        FROM users 
        WHERE username = ?
        `, [username])

    if (!user) {
    console.log("NO_USER")
    res.status(400).json({message: "NO_USER"})
    }

    const isPasswordCorrect = bcrypt.compareSync(password, user.password) // true or false
    if (!isPasswordCorrect) {
    res.status(400).json({message: "INVALID_PASSWORD"})
    }

    const token = jwt.sign({ userId: user.id }, 'secretKey');
    res.status(200).json({message: 'LOGIN_SUCCESS', token: token})
})

 

 

참고

저스트코드 노드 + express 톺아보기

 

'웹 프로그래밍 > Javascript' 카테고리의 다른 글

jest로 unit test하기  (0) 2022.10.02
Layered Pattern  (0) 2022.10.01
Middleware  (0) 2022.09.28
에러/오류 핸들링  (0) 2022.09.28
자바스크립트와 비동기 프로그래밍(콜백 함수, Promise, async/await)  (0) 2022.09.27