Change Password #02

0_CyberLover_0·2022년 4월 27일
0

Node.JS #05

목록 보기
14/19

먼저 mongo로 가서 데이터를 삭제 해 주었다.

use wetube를 커맨드 입력해주고 db.sessions,db.users에서 remove({}) 해주면 된다.

그리고 계정을 하나 만들어 본다. 다른건 다 작동하나 비밀번호 수정은 아직 안된다.

그전에 새로운 middelware를 만들어 본다.

깃허브로 로근인하지 않은 사람들을 위한 middleware이다.

깃허브로 로그인하면 무언가를 보여주지 않거나 보여주거나 하는 middelware이다.

비밀번호를 가지고 있는 유저로부터 url을 보호하는 middleware를 만들 수도 있다.

 if (req.session.user.socialOnly === true) {
    return res.redirect("/");
  }

이 코드를 여러 번 반복한다면 middleware를 만들어도 된다. 현재는 아니니 그냥 둔다.

그리고 inputname을 넣는걸 잊어서는 안된다. 그러면 백엔드에서는 읽을수 없다.

typepassword로 해주는거 잊지 않는다. 이제 이 name들을 controller에서 사용 할거다.

extends ../base

block content 
    form(method="POST")
        input(placeholder="Old Password",name="oldPassword", type="password", requeired)
        input(placeholder="New Password",name="newPassword", type="password", requeired)
        input(placeholder="New Confirm Password",name="newPasswordConfirmation", type="password", requeired)
        input(type="submit", value="Change Password")

바로 여기(pastChangePassword)에서 사용 할거다.

export const postChangePassword = (req, res) => {

name들을 req.body안에서 가져온다.

const { oldPassword, newPassword, newPasswordConfirmation } = req.body;

그리고 현재 로그인한 사용자가 누군지 알아야 한다. 지금은 누가 비밀번호를 변경하려는지 모른다.

이 경우에는 postEdit에서 사용 했던 방식을 쓰면 된다.

누가 로그인 했는지 알려면 userid를 확인해야 되기 때문이다.

그리고 비밀번호에 관련 된것들도 바꿔주면 된다.

export const postChangePassword = (req, res) => {
  const {
    session: {
      user: { _id },
    },
    body: { oldPassword, newPassword, newPasswordConfirmation },
  } = req;

session에서 현재 로그인된 사용자를 확인하고 form에서 정보를 가져오는거다.

form에서는 사용자에게 누구냐고 묻지 않는다. 다른 사용자의 정보를 변경 할수 없어야 한다.

이제 사용자가 존재하는지 확인하고 async를 하는거 잊지 말고 그리고 비밀번호를 변경해주면 된다.

새 비밀번호와 비밀번호 확인이 같은지 확인해야 한다.

export const postChangePassword = async (req, res) => {
  const {
    session: {
      user: { _id },
    },
    body: { oldPassword, newPassword, newPasswordConfirmation },
  } = req;
  if (newPassword !== newPasswordConfirmation) {
    return res.render("users/change-password", {
      pageTitle: "Change Password",
      errorMessage: "The new password does not match the confirmation.",
    });
  }
  // send notification
  return res.redirect("/");

getChangePassword에서 썼던 코드를 가져와서 에러메세지를 추가 해주었다.

나중에는 이렇게 에러 메세지를 보내는 대신에 어떤 template에도 보낼수 있는 알림을 사용 할거다.

block content 
    if errorMessage 
        span=errorMessage

그리고 change-password에도 에러메세지를 만들어 준다.

테스트 해보면 잘 된다. 그러나 브라우저는 맞은걸로 알고 비밀번호를 저장하겠냐고 물어본다.

전에도 해봤던 status code를 변경해야 한다.

    return res.status(400).render("users/change-password", {

join에서 에러가 생길때 했었다. 브라우저는 status code를 주시하고 있다.

이번에 할건 기존 비밀번호가 정확한지 확인해야 한다.

bcrypt로 했었다. 적용해 본다.

export const postChangePassword = async (req, res) => {
  const {
    session: {
      user: { _id, password },
    },
    body: { oldPassword, newPassword, newPasswordConfirmation },
  } = req;
  const ok = await bcrypt.compare(oldPassword, password);
  if (!ok) {
    return res.status(400).render("users/change-password", {
      pageTitle: "Change Password",
      errorMessage: "The current password is incorrect.",
    });
  }

const ok = await bcrypt.compare() 넣어준다.

oldPasswordplain text로 넣고 암호화된 password도 넣어준다.

session에 현재 로그인된 user 정보가 있다. userpassword정보를 포함한 object이다.

그래서 사용자가 form으로 보낸 비밀번호와 session에 있는 비밀번호를 비교 한다.

if(!ok) ok가 아니면 똑같은걸 return한다.

그리고 "기존 비밀번호가 일치하지 않다"로 에러 메세지를 바꿔 준다.

만약 모든게 일치한다면 비밀번호를 변경하도록 해준다. 그 전에 에러 메세지가 잘 나오는지 확인해 본다.

에러 메세지가 잘 나온다.

그 다음 단계는 비밀 번호를 변경해준다.

예전에 비밀번호를 저장하던게 있다.

User.js에서

userSchema.pre("save", async function () {
  this.password = await bcrypt.hash(this.password, 5);
});

비밀번호를 보내고 저장하면 저 함수가 비밀번호를 hash해준다.

함수를 작동시키려면 두가지를 해줘야 한다. 하나는 pre save middleware를 거치고

User.create를 사용하는거다.

pre save middleware가 비밀번호를 hash해주고 있다는걸 기억한다.

그리고 user.save()를 해도 pre save middleware를 작동시킬거다.

export const postChangePassword = async (req, res) => {
  const {
    session: {
      user: { _id, password },
    },
    body: { oldPassword, newPassword, newPasswordConfirmation },
  } = req;
  const ok = await bcrypt.compare(oldPassword, password);
  if (!ok) {
    return res.status(400).render("users/change-password", {
      pageTitle: "Change Password",
      errorMessage: "The current password is incorrect.",
    });
  }
  if (newPassword !== newPasswordConfirmation) {
    return res.status(400).render("users/change-password", {
      pageTitle: "Change Password",
      errorMessage: "The new password does not match the confirmation.",
    });
  }
  user.save();

하지만 이 경우에는 현재 user가 없다. 그래서 먼저 user를 찾아야 한다.

save()함수를 써야 하니까 session에서 로그인된 user를 찾아야 한다.

user를 찾으면 save()함수를 사용 할수 있다.

if (newPassword !== newPasswordConfirmation) {
    return res.status(400).render("users/change-password", {
      pageTitle: "Change Password",
      errorMessage: "The new password does not match the confirmation.",
    });
  }
  const user = await User.findById(_id);
  console.log("Old pw", user.password);
  user.password = newPassword;
  console.log("New unhashed pw", user.password);
  await user.save();
  console.log("New pw", user.password);
  // send notification
  return res.redirect("/");
};

const user = await User.findById를 사용하여 session에서 _id를 찾을수 있다.

user를 찾았으니 user.password = newPassword; 해주고 그 다음 user.save()를 사용하는 거다.

user.save()를 하면 pre save가 작동한다. 이걸 작동시키려는 이유는 새로운 비밀번호를 hash하기 위해서이다.

확인할수 있게 console.log(user.password)를 해준다.

매 부분마다 확인할수 있게 console.log(user.password)를 넣어 주었다.

그리고 save()promise이다. 그래서 await를 붙여 주었다.

DB에 무언가를 저장하는데는 시간이 걸려서 그렇다.

console.log가 새 비밀번호와 hash된 새 비밀번호를 보여줄거다.

예전 비밀번호도 확인해 본다. 테스트 해서 확인해 보겠다. 잘 된것같다. 콘솔에서도 확인해 본다.

Old pw $2b$05$k0gX8agEfRX0ufJ7H1lOWe99BpAEvMJ9qGPZZ389cRQHCJampVDl2
New unhashed pw 123
New pw $2b$05$.h1dcDGwaXZpF6iS9CC1Pehmb3kwZwOT9aeGozlsZAGAJm9yHgf/a

기존 비밀번호, hash되지 않은 새 비밀번호 그리고 hash된 새 비밀번호가 나온다.

그리고 사용자가 비밀번호를 바꾸면 로그아웃 시키는 기능도 넣어본다.

다시 로그인 하도록 로그아웃 시키는 거다.

const user = await User.findById(_id);
  console.log("Old pw", user.password);
  user.password = newPassword;
  console.log("New unhashed pw", user.password);
  await user.save();
  console.log("New pw", user.password);
  // send notification
  return res.redirect("/users/logout");

테스트 해보니 비밀번호를 바꾸면 로그아웃 시킨다. 그러나 해당 강의에서는 안된다.

그래서 따라 해보기로 한다.

안되는 이유는 비밀번호 변경을 잘목했거나 기존 비밀번호를 session에 있는 비밀번호와 비교해서 그럴수도 있다.

session을 업데이트해주지 않아서 그렇다. session에 있는 hash된 비밀번호가 기존 비밀번호와 일치하는지 확인하고 있다.

그러니 session을 업데이트 해준다.

 const user = await User.findById(_id);
  user.password = newPassword;
  await user.save();
  req.session.user.password = user.password;
  // send notification
  return res.redirect("/users/logout");
};

req.session.user.password = user.password;이렇게 해준다.

왜냐하면 여기서는 비밀번호가 hash 돼 있기 때문이다. DBsession 두개의 저장소를 사용하고 있다.

session에서 정보를 받으면 업데이트도 해줘야 한다.

form에서 가져온 비밀번호랑 현재 로그인된 사용자의 비밀번호를 비교하고 있는걸 기억한다.

다른 방법도 있다.

export const postChangePassword = async (req, res) => {
  const {
    session: {
      user: { _id },
    },
    body: { oldPassword, newPassword, newPasswordConfirmation },
  } = req;
  const user = await User.findById(_id);
  const ok = await bcrypt.compare(oldPassword, user.password);
  if (!ok) {
    return res.status(400).render("users/change-password", {
      pageTitle: "Change Password",
      errorMessage: "The current password is incorrect.",
    });
  }
  if (newPassword !== newPasswordConfirmation) {
    return res.status(400).render("users/change-password", {
      pageTitle: "Change Password",
      errorMessage: "The new password does not match the confirmation.",
    });
  }

  user.password = newPassword;
  await user.save();
  return res.redirect("/users/logout");
};

여기에 user를 넣고 항상 user.password를 해주는거다.

user를 찾아서 DB에 있는 가장 최근의 비밀번호를 사용하는거다.

session에서 password를 가져오지 않아도 된다. 똑같이 작동한다.

profile
꿈꾸는 개발자

0개의 댓글