웹 풀사이클 데브코스 TIL 8주차 DAY 3

갱갱·2024년 1월 3일
0

데브코스 TIL

목록 보기
17/24
post-thumbnail

프로그래머스 데브코스, 국비지원교육, 코딩부트캠프

🚩 프로그래머스 데브코스 웹 풀사이클 과정 8주차 DAY 3


어제 하다가 끝내지 못한 UserController.js를 완성시켰다. 로그인 기능과 비밀번호 초기화 요청, 비밀번호 초기화가 있다. 생각해보니 로그아웃 기능이 없는데 이건 내가 따로 만들어 봐야겠다. 사실 지금도 강의랑은 좀 다르게 짜고 있지만

💡 Express로 로그인 구현


const signin = (req, res) => {
    const { email, password } = req.body;
    const sql = 'select * from users where email = ?';

    conn.query(sql, email, (err, results) => {
        if (err) {
            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
                message: '서버 에러',
            });
        }

        const signinUser = results[0];

        if (!signinUser) {
            return res.status(StatusCodes.UNAUTHORIZED).json({
                message: '해당하는 이메일이 존재하지 않습니다.',
            });
        }

        try {
            const hashedPassword = crypto.pbkdf2Sync(password, signinUser.salt, 10000, 10, 'sha512').toString('base64');

            if (hashedPassword === signinUser.password) {
                const token = jwt.sign(
                    {
                        email: signinUser.email,
                    },
                    process.env.PRIVATE_KEY,
                    {
                        expiresIn: '1h',
                        issuer: 'minkyung',
                    }
                );
                res.cookie('token', token, {
                    httpOnly: true,
                    secure: true,
                    sameSite: 'none',
                });
                return res.status(StatusCodes.OK).json({
                    message: '로그인 성공',
                    token: token,
                });
            } else {
                return res.status(StatusCodes.UNAUTHORIZED).json({
                    message: '비밀번호가 일치하지 않습니다.',
                });
            }
        } catch (err) {
            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
                message: '비밀번호 해싱 중 문제가 발생하였습니다.',
            });
        }
    });
};

비동기로 코드를 짠다면 좀 더 보기 좋아질 것 같은데 내가 아직 비동기 부분에 대해서 좀 미숙한지라... 조금 더 공부한 후 전체적으로 리팩토링을 할 예정이다.

  1. 해당하는 이메일이 존재하는지 확인, 없으면 Unauthorized 반환
  2. 이메일이 존재한다면 비밀번호 일치하는지 확인, 일치하지 않으면 Unauthorized 반환
  3. 데이터베이스에 저장한 솔트값을 꺼내서 해시 후 비교
  4. jwt 생성, 만료 시간은 1시간
  5. 로그인 성공 시 토큰 반환
  6. 비밀번호 해싱 시 문제가 생길 것을 고려하여 try/catch 사용

에러 정상 반환

로그인 성공 시 토큰 정상 반환

위 경우는 솔트값이 포함되지 않은 계정으로 로그인 시 발생하는 에러

💡 Express로 비밀번호 재설정 구현


  1. 비밀번호 초기화 요청
const pwdResetRequest = (req, res) => {
    const { email } = req.body;
    const sql = 'select * from users where email = ?';

    conn.query(sql, email, (err, results) => {
        if (err) {
            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
                message: '서버 에러',
            });
        }

        const user = results[0];

        if (!user) {
            return res.status(StatusCodes.UNAUTHORIZED).json({
                message: '해당하는 이메일이 존재하지 않습니다.',
            });
        } else {
            return res.status(StatusCodes.OK).json({
                message: '이메일 발송 성공',
                email: email,
            });
        }
    });
};
  1. 비밀번호 초기화
const pwdReset = (req, res) => {
    const { email, password } = req.body;

    const salt = crypto.randomBytes(10).toString('base64');
    const hashPwd = crypto.pbkdf2Sync(password, salt, 10000, 10, 'sha512').toString('base64');

    const sqlUpdate = 'update users set password = ?, salt = ? where email = ?';
    const sqlSelect = 'select * from users where email = ?';
    const values = [hashPwd, salt, email];

    conn.query(sqlSelect, email, (err, results) => {
        if (err) {
            return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
                message: '서버 에러',
            });
        }

        const user = results[0];

        if (!user) {
            return res.status(StatusCodes.UNAUTHORIZED).json({
                message: '해당하는 이메일이 존재하지 않습니다.',
            });
        }

        const hashedNewPassword = crypto.pbkdf2Sync(password, user.salt, 10000, 10, 'sha512').toString('base64');

        if (hashedNewPassword === user.password) {
            return res.status(StatusCodes.BAD_REQUEST).json({
                message: '새 비밀번호는 기존 비밀번호와 달라야 합니다.',
            });
        }

        conn.query(sqlUpdate, values, (err, results) => {
            if (err) {
                return res.status(StatusCodes.INTERNAL_SERVER_ERROR).json({
                    message: '서버 에러',
                });
            }

            if (results.affectedRows > 0) {
                res.status(StatusCodes.OK).json({
                    message: '비밀번호 초기화 성공',
                });
            } else {
                res.status(StatusCodes.BAD_REQUEST).json({
                    message: '비밀번호 초기화 실패',
                });
            }
        });
    });
};

마찬가지로 비동기를 사용하지 않아 코드가 조금 지저분...하다. 빨리 공부해서 리팩토링해야지😭

  1. 비밀번호 재설정 요청 시에는 확인 메일을 전송하기 때문에 가입 시 입력했던 메일이 필요하다. 이 부분은 nodemailer를 이용해서 차후 추가할 예정, 지금은 우선 메일이 맞는지만 확인
  2. 메일이 존재한다면 해당하는 메일 주소를 반환
  3. 비밀번호 재설정 부분에는 혹시 몰라 해당하는 이메일이 존재하지 않을 때의 예외 처리를 해두긴 했는데... 생각해보니 이메일 반환 후 바로 페이지가 넘어갈텐데 굳이 필요하려나? 이 부분은 좀 더 생각해보자
  4. 입력한 새 비밀번호가 기존 비밀번호가 같다면 에러 반환
  5. 비밀번호 초기화 성공 시 200 OK

이메일 존재 시 해당 이메일 주소 반환

이메일 존재하지 않으면 Unathorized 반환

새로운 비밀번호 초기화에 성공한다면

이게 기존 비밀번호 (해시화되어 저장)

변경 후 다시 조회하면 비밀번호와 솔트값 모두 바뀌어있다.

변경 이후 변경된 비밀번호로 로그인 시 정상적으로 로그인이 되는 걸 확인할 수 있다.

기존 비밀번호와 똑같은 비밀번호를 입력 후 수정하려고 하면 Bad request를 반환한다.

profile
괜찮은 개발자가 되어 보자

0개의 댓글