Login #02

0_CyberLover_0·2022년 4월 15일
0

Node.JS # 04

목록 보기
10/19

지금 까지 만든건 다 한번씩 해본거다.

그래서 처음 사용 했을때보다 좀더 빨리 만들수 있었다.

전에 써봤던 route,get,post를 쓰고 있고 req.body도 사용해봤다.

mongoose도 마찬가지이고 render도 하고 있다.

보다시피 이 모든걸 같이 구현하게 되면 정말 멋진 기능을 만들어 낼수 있다.

예상했겠지만 가장 중요한 controller중 하나는 post이다.

getJoin controller는 페이지를 render만 하지만

postJoin은 실제로 사용되어지는 기능을 담당하고 있다.

데이터를 다루고 에러 처리, 유효성 체크 등을 하면서 DB와 통신하고 있다.

지금까지 배웠던 모든 것들은 이렇게 개발할 수 있게 만들기 위한 준비 과정이었다.

usersController에서 모든걸 사용하고 있다. mongoose,middlewares,redirections,urls...

controllers,post,get,body.. 이런 것들을 다 쓰고 있다.

지금 이 모든걸 사용하는 정점에 있는거다. 배웠던걸 다 적용하고 있는 거다.

password가 일치하는지 체크를 해야한다.

이 부분을 어떻게 구현할지 정말 궁금하다. 패스워드가 어떤 모양으로 해싱되었는지

모르는데 어떻게 일치하는지 알수 있을까?

DB를 보면 해싱된 패스워드가 있다. 전에 말했다시피 입력값이 같으면 항상 같은 해시값이

나온다고 했다. 해싱은 결정적 함수이라서 그렇다.

항상 같은 거다. 입력값이 같으면 항상 같은 출력값을 가지게 되는 거다.

그래서 DB에 있는 패스워드를 해석할 수 없다. 유저만이 알거다.

그런데 그 패스워드의 해시값을 알고 있다. 그래서 유저가 로그인을 하려 할때

뭘 하면 되냐면 로그인할 때 유저가 입력한 패스워드를 가져다가 해싱을 하면 되는거다.

그러면 패스워드를 해싱하고 나온 해시값을 비교하면 되는거다.

그리고 만약 해시값이 서로 일치하면 유저가 정확한 패스워드를 입력했다고 알수 있다.

우선 계정을 생성하면 이 과정이 진행될거다. 패스워드를 보내면 해싱해서 값을 저장하는 거다.

그리고 로그인 할때도 패스워드를 보낼텐데 그 패스워드는 이해할수가 없다.

그렇지만 해시값이 뭔지는 알고 있다. 그리고 입력값이 바뀌지 않는 이상 출력값이 절대 바뀌지 않는다는 것도 알고 있다.

그래서 유저가 로그인 할때 보낸 패스워드를 해싱하고 비교만 해주면 된다.

해시값은 항상 같을거니까 DB에 있는 해시값과 비교를 했을때 일치한다고 나올거다.

같은 문자열이니까 만약 로그인할때 다른 패스워드를 입력하면 DB에 있는 해시값을 가지고 비교했을때

해시값이 많이 다를테니까 패스워드가 틀렸다고 알수 있다. 다시 말하지만 원래 패스워드가 뭔지 알 필요가 없다.

패스워드의 해시값을 알고 있으면 되는 거다. 유저가 로그인을 할때 입력한 패스워드를 해싱하고

그 값이 DB에 있는 해시값과 같은지 비교하면 되는거다.

이 방법으로 원래 패스워드를 알지 못하더라도 패스워드가 일치하는지 알수 있다.

그래서 사용할 함수는 compare라는 거다. 이것도 bcrypt에 내장 되어 있다.

https://www.npmjs.com/package/bcrypt

// Load hash from your password DB.
bcrypt.compare(myPlaintextPassword, hash).then(function(result) {
    // result == true
});

여기를 보고 compare()를 쓰기만 하면 된다. myPlaintextPassword에 들어가는 건

유저가 입력한 패스워드이고 hash에는 DB에 있는 해시값을 넣어주면 된다.

다시 말하면 myPlaintextPassword에는 유저가 입력한 패스워드를 넣고

login form 에서 입력한 패스워드를 말하는거다. 이게 틀렸을지 맞았을지는 아무도 모른다.

그리고 hash에는 DB에 있는 패스워드를 넣을거다. 그러면 bcrypt가 비교를 한다.

bcrypt가 이 패스워드를 해싱하고 해시값과 비교할거다.

알다시피 입력값이 바뀌지 않으면 출력 값이 항상 같다.

여기에 나와 있다시피 resulttrue를 받게 된다.

아니면 이렇게 할수도 있다.

async function checkUser(username, password) {
    //... fetch user from a db etc.

    const match = await bcrypt.compare(password, user.passwordHash);

    if(match) {
        //login
    }

    //...
}

이걸로 만들어 보도록 한다.

우선 유저가 로그인하려는 계정이 뭔지부터 알아햐 한다. 왜냐하면 로그인 form에 입력한

패스워드인 Plain text가 필요하고, 해시도 필요하다.

어떤 계정으로 로그인하려고 하는 건지 알아햐 한다. 그러면 user를 먼저 찾아본다.

export const postLogin = async (req, res) => {
  const { username, password } = req.body;
  const user = await User.findOne({ username });
  if (!user) {
    return res.status(400).render("login", {
      pageTitle: "Login",
      errorMessage: "An account with this username does not exists.",
    });
  }
  console.log(user.password);
  res.end();

user를 먼저 찾아보고 req.body에서 가져온 username을 가지는 User를 찾을거다.

그런데 user를 두번씩이나 찾고 있다. 한번은 user가 존재하는지 체크하고

두번째로는 DB에서 user를 가져오고 있다. 한곳에서 같이 하는게 좋을것 같다.

그래서 exists대신 user를 가져오도록 한다. user가 존재하지 않는건

(!user)로 알수 있게 되었다. 이제 바로 쓸수 있는 user가 생겼다.

그러면 user.passwordconsole.log하고 잘나오는지 확인해 본다.

콘솔을 확인해 보면 해싱된 패스워드값이 출력 되었다. DB에서 가져온거다.

이제 bcryptcompare를 쓰기만 하면 된다. 그러면 bcryptimport한다.

import bcrypt from "bcrypt";
export const postLogin = async (req, res) => {
  const { username, password } = req.body;
  const pageTitle = "Login";
  const user = await User.findOne({ username });
  if (!user) {
    return res.status(400).render("login", {
      pageTitle,
      errorMessage: "An account with this username does not exists.",
    });
  }
  const ok = await bcrypt.compare(password, user.password);
  if (!ok) {
    return res.status(400).render("login", {
      pageTitle,
      errorMessage: "Wrong password",
    });
  }

그리고 compare를 쓰고 function에는 유저가 입력한 패스워드를 넣어준다.

그러면 body에서 가져온 password를 넣는다. 그리고 암호화 된 패스워드를 넣어준다.

그건 바로 user.password이다. 그리고 만약에 ok가 아닐때 위에선 사용한 에러메세지

복사해 주었다. 그리고 메세지만 다르게 설정 하였다. 이렇게 하면 패스워드를 체크하게 만든거다.

그리고 살펴보면 pageTitle을 2번씩이나 사용하고 있는데 중복 되는 내용이니깐

변수를 만들어주었다.

  const pageTitle = "Login";

이제 잘 작동하는지 테스트해본다. 틀린 패스워드를 입력하면 에러메세지가 나오고

맞는 패스워드를 작성하면 연결이 끊긴다. res.end()를 했기 때문에 그렇다.

이게 실행되면 에러가 없었다는 거다. 그렇다는건 성공적으로 패스워드를 비교했다는거다.

이제 패스워드가 맞는지 비교 할수 가 있는거다. 원래 패스워드가 뭔지 알 필요도 없다.

postLogin controller를 만들었다. 다음으로는 실제로 유저를 로그인 시켜야 한다.

홈 화면으로 갈수 있도록 res.redirect("/")return하고

console.log("LOG USER IN!!! COMING SOON~!");
  return res.redirect("/");
};

"로그인이 되었다" 고 알려주는 console을 만들었다. 왜냐하면 로그인했다는 말은

접속한 유저가 누구인지 기억하고 있다는 거니까 말이다.

이걸 위해서는 쿠키와 세션에 대해 얘기해야 한다. 테스트를 해본다.

오류가 나면 메세지를 띄워주고 패스워드가 맞으면 로그인이 된다. 홈화면으로 이동하고

패스워드를 저장할건지 물어본다. 왜냐하면 redirect를 했는데 이 때 브라우저는 아무

문제가 없다고 판단한거다. 문제가 있는 상태 코드를 보내지도 않았고 말이다.

그래서 브라우저가 이게 맞는 패스워드라고 판단한거다. 아직 실제로 로그인을 하지는 않았지만 말이다.

그리고 콘솔로 가보면 잘 출력 되고 있다. "LOG USER IN!!! COMING SOON~!"

다 잘 동작되고 드디어 로그인 하는 걸 만들었다. 적어도 어떤 유저가 로그인 한건지는 알수 있지만

이제 실제로 로그인을 구현해야 한다. 그런데 이걸 위해서는 세션에 대해 알아야 한다.

profile
꿈꾸는 개발자

0개의 댓글