세션 구현 시도

 

https://codelist.tistory.com/62

 

[Vue.js, Node.js] 쿠키로 로그인/로그아웃 구현

여는 글 쿠키는 보안상 좋지 않아서 쿠키만으로는 로그인/로그아웃을 구현하기엔 좋지 않다. 대부분은 세션+쿠키를 사용하는 것 같다. 그래서 나도 처음엔 세션부터 구현하려고 했다. 세션에는

codelist.tistory.com

 

 

이제 DB 삭제가 되기 때문에 내가 세션을 자체적으로 구현하려고 했다.

 

로그인 시 DB 생성 -> DB 생성 시 TTL 사용해서 지정 시간 뒤에 자동으로 세션 값 삭제

 

근데 이틀 동안 TTL에서 막혀서 그냥 라이브러리 써야겠다 싶었는데 1번 글에서 만났던 문제를 또 마주하게 됐다.

 

 

        req.session.userinfo = users.phone
                         ^

TypeError: Cannot read property 'userinfo' of undefined

이런 에러가 나는데 다른 사람들은 잘만 사용하던데 난 왜 안될까...

 

일단 req.session 자체가 undefined라 키도 당연히 오류나는 것 같아서 req.session undefined 구글링부터 해보고 있다...

 

 

 

 

+22.1.4 해결

 

 

req.session undefined를 구글링하고 있는데 아래 포스팅을 보게 됐다.

 

https://jaroinside.tistory.com/15

 

1. express-session

express 사용시 express-session 을 사용하는 경우가 많습니다. 이번에 한 블로그의 공부를 따라하던중, express-session 을 사용하였는데, 로그인 구현 관련이었습니다. 로그인을 하면서 session을 저장하기

jaroinside.tistory.com

 

그래서 순서를 바꿨더니 페이지 접속 시 에러가 생겼다.

 

TypeError: Secret string must be provided.

 

사실 이 에러는 처음 보는 에러가 아니었다.

 

세션 이것저것 해보면서 몇 번 만나봤던 에러인데 나는 분명히 secret을 명시해뒀기 때문에 그냥 이상하고 뜬금없는 에러라고 생각해서 무시했는데 이번에는 저 에러에 대해서 구글링을 해봐야겠다는 생각이 들었다.

 

그리고는 아래 글을 보고 해결할 수 있었다.

 

https://stackoverflow.com/questions/38326191/node-js-session-login

 

Node.js session login

Node.js does not seem to be remembering my session. I am using Express with cookie-parser and express-session as middleware. In my application, I use Mongo to keep usernames and passwords. The

stackoverflow.com

 

cookieParser 설정에 대한 내용인데 먼저 글을 읽어보고 나는 cookieParser에 딱히 설정한 게 없을텐데..? 하면서 내 코드를 봤더니...

 

app.use(cookieParser({sameSite : 'none', secure : true}));

 

secure가 true로 되어있었다.

 

배포하고나서 쿠키 안될 때 건드렸던 부분인데 여기서도 secure가 문제일 줄 몰랐다.

 

그래서 secure를 지웠으나 그래도 똑같은 에러가 생겼는데 에러를 다시보면 secret을 제공해야한다- 라는 내용이므로 아래처럼 작성하니 session이 작동했다.

 

app.use(cookieParser('secret'));

 

 

session이 안되는 건 당연히 session 문제일 줄 알았는데 cookieParser가 문제일 줄 몰랐다.

 

 

기존 코드도 다시 보고 없던 에러가 생기면 유심히 봐야겠다...

 

 

++session store 설정 참고 코드

https://kimjingo.tistory.com/43

 

 

마이페이지-개인정보수정 기능을 구현하고 있다.

 

이전에 DB 값 삭제(탈퇴) 구현에서 한참을 애먹었었다.

 

https://codelist.tistory.com/71

 

[Node.js, Vue.js, MongoDB] ::디버깅 실패:: 세션 구현하기

기록 파일에 세션을 저장하는 것도 여전히 안되고 그나마 DB에 세션을 저장하는 게 될 가능성이 있다고 판단해서 여기를 집중적으로 해봤는데 여전히 안된다. 파일에 세션 저장하는 것처럼 아예

codelist.tistory.com

 

https://codelist.tistory.com/81

 

[Node.js, Vue.js] 탈퇴 기능(mongodb)

나는 mongodb를 사용하기 때문에 remove(구버전이라 이제 사용 X), deleteOne(), deleteMany() 등의 명령어를 사용해서 document를 삭제해야하는 데 삭제가 안됐다. 해결하고보니 두 가지의 문제점이 있었다.

codelist.tistory.com

 

그래도 해결했으니 설령 UPDATE 기능이 안되더라도 대처할 수 있겠다 싶었는데 UPDATE는 웬만한 시도를 다 해봤으나 동작하지 않았다.

 

UPDATE에서 막힐 거라고는 생각도 못했었는데 DELETE보다 안 되니까 이쯤되면 사실 DELETE도 완벽하게 구현된 게 아닌가? 라는 생각이 들었다.

 

일단 update, updateOne, findAndModify, findOneAndUpdate 등등 시도해봤고 앞에 컬렉션이름.~, db이름.~ 등 다 갖다붙여봤고 DELETE 구현할 때 시도해봤던 FindOne/Find 사용해보고 아예 없애보기도 했는데 5일동안 구현하지 못했다.

 

 

해결

 

참고한 곳 : https://velog.io/@yejineee/Mongoose-Atomic-Update-%EB%B0%A9%EC%8B%9D%EC%9D%84-%EC%B0%BE%EC%95%84%EC%84%9C

 

Mongoose Atomic Update 방식을 찾아서

Account 컬렉션은 transaction 도큐먼트의 objectId를 자신의 필드인 transactions에 추가하여야 한다. 그 과정은 다음 두 가지 일을 해야 한다. Account Object Id로 Account 도큐먼트를 찾는다. 그 도큐먼트의 trasa

velog.io

 

 

 

backend/routes/index.js

router.post('/mypage', (req, res) => { //마이페이지 정보 수정
  User.findOne({name : req.cookies.session},(err, users) => { 
      if(!req.cookies.session || err) {  // 쿠키 없거나 에러 났을 때
        res.redirect('/');
      } else {
        users.updateOne({$set : {phone : req.body.phone}}).exec(); // 여기!!
        res.redirect('/mypage');
      }
  }) 
})

 

마지막에 .exec() 붙이니까 된다.

 

저번 글(https://codelist.tistory.com/80)에서 마무리에 

 

제일 좋은 건 백엔드->프론트로 response를 전달해서 프론트에서 response를 받아 alert를 띄우는 거지만 지금의 나에겐 이 방식이 최선인 것 같다.

 

라고 했었다.

 

알고보니 axios 문제였고 해결 된 지금은 내가 원하던 대로 백엔드->프론트로 response를 전달해서 프론트에서 response를 받아 alert를 띄우는 게 가능해졌다.

 

원래는 아래처럼 작성했었다.

<script>

    methods : {
        login(){
            this.axios.post('http://olrang.shop:3000/login',  { //이 줄의 this가 문제였음
                withCredentials: true,
            id : this.id,
            password : this.password
                
            }).then((response) => { 
                this.response = response
                
            }).catch((error) => {  
                this.error=error    
            })
        },
    }
}
</script>

 

backend로 전송은 되니까 코드 자체에 문제가 없다고 생각했다.

 

받기가 안되는 건 nodejs의 문제거나 then에서 받는 코드를 잘못 작성했을 거라고만 생각했지 axios 코드 자체가 잘못됐다고 생각하지 못했다.

 

 

<script>

import axios from 'axios'

    methods : {

        login(){
            axios.post('http://olrang.shop:3000/login',  { 
                withCredentials: true,
            id : this.id,
            password : this.password
                
            }).then((response) => { 
                this.response = response
            
                
            }).catch((error) => {  
                this.error=error    
            
                
            })

        },

        

    }

}
</script>

 

위 코드로 받으니까 then부분이 잘 동작했다.

 

물론 alert도 잘 띄운다.

 

이제야 드디어 내가 원하던 방식으로 alert를 띄울 수 있게 됐다.

 

DB 연결할 때 참고한 포스팅에서 보고 8월 중순부터 this 붙여서 썼는데 처음부터 써왔던 코드도 의심해봐야하는구나.. 라고 생각했다.

 

 

DB를 사용하고 있는 만큼 생성과 삭제, 수정 기능 전부를 구현해보고 싶었다.

 

그래서 이제 수정 기능만 구현하면 되는데 마침 마이페이지-개인정보수정 기능을 넣으면 딱이겠다, 싶어서 만들기 시작했다.

 

다른 공식 페이지들을 보면 개인정보 수정하러 들어가면 폼에 기존 값이 입력되어져있다.

 

즉, 수정 페이지에 들어가면 화면에 DB값을 입력시켜 놓아야 한다는 것이다.

 

 

 


 

 

문제1  json 전송

 

DB값을 가져와 VUE에 뿌려야했는데 이 부분이 문제였다.

 

보통은 res.json/res.send로 json을 vue로 보내는 방식을 쓰는 듯한데 내가 res.json/send를 쓰면 페이지가 아래처럼 된다.

 

 

빈 화면에 값만 출력된다.

 

새로고침해도 저런 화면으로 뜨는 데 테스트를 여러 번 해보니 get 방식으로 전송하는 페이지를 새로고침할 경우 새로고침하면 그 페이지의 get 요청이 실행되어버린다.

 

그래서 처음엔 새로고침을 막을까도 생각했지만 근본적인 해결이 안되기때문에 고민하다가 도메인 연결할 때 참고했던 포스팅에서 frontend랑 backend 경로를 다르게 설정했던 게 떠올랐다.

 

frontend에서는 /login로, backend에서는 /api/login로 설정하고 frontend랑 backend랑 ip도 다르게 설정하셨던 것 같은데 처음 봤을 때는 왜 굳이 이렇게 설정하지? 라는 생각이 들었는데 구글링하다보니 이렇게 설정하시는 분이 많아서 나도 이렇게 해야하나..? 라는 생각까지 들었다.

 

그래도 난 저렇게 설정 안해도 문제 없었고 굳이? 라는 생각이 들어서 넘겼는 데 갑자기 이 기억이 생각나면서

 

데이터를 /mypage로 보내니까 json이 /mypage에 띄워져버리는 거라면 /api/mypage로 json을 보내되 출력은 /mypage에서 하면?

 

라는 생각이 들어서 해봤는데 뜻밖에도 내 가정대로 동작했다.

 

게다가 새로고침해도 멀쩡하고 /api/mypage가 눈에 보이지도 않았다.

 

사실 이렇게 해결하는 게 맞는 지 모르겠지만 다른 분들이 backend에서 /api 를 덧붙이는 이유가 서버동작처리용 경로이라고 추측했기 때문에 저런 가정을 해봤다.

 

++확인해보니 주소창에 직접 /api/mypage를 칠 경우 빈 화면에 json이 그대로 보여서 완전히 해결된 상태는 아닌 것 같은데 어떻게 고쳐야 할 지 모르겠다...

 

 

 

 

문제2  접속 시 DB값 띄우기

 

 

 이건 문제 1을 해결하려다가 우연히 해결하게 됐다.

 

vue의 created()를 쓰면 화면 접속 시 DB값이 띄워져 있는 상태로 볼 수 있다.

 

화면을 그릴 때 응답을 받아서 성공하면 화면을 띄우는 방식이라 내가 원하던 대로 동작한다.

 

 

/components/mypage.vue

<script>
import axios from 'axios'

export default {
    name : "MyPage",
    data(){
        return{
            mydata:''
        }
    },
        created() {
            axios.get('http://olrang.shop:3000/api/mypage',  { 
                withCredentials: true,
                headers : {
                    "Content-Type" : "application/json"
                },
            }).then((response) => { 
                this.mydata = response.data.phone
                
            }).catch((error) => {  
                this.error=error    

            })
        },
    }

</script>

 

 

/routes/index.js

router.get('/api/mypage', (req, res) => { //마이페이지 정보 수정
  User.findOne({name : req.cookies.session},(err, users) => { 
      if(!req.cookies.session || err) {  // 쿠키 없거나 에러 났을 때
        res.redirect('/');
      } else {
        res.set('Content-Type', 'application/json');
          var mydata = {phone : users.phone};
          res.json(mydata);
      }
  })
})

 

 

app.js

app.use('/api/mypage');

 

 


 

 

사실 구현하면서 안되는 부분이 많았다. (빈 화면에 json이 뜨는 문제는 몇 달전부터 인지하고 있었고 지금은 해결되었지만 axios에서 전송만 되고 받기가 안되는 문제가 있었다.)

 

안 고쳐지는 부분이라고 생각해서 놔뒀고 더 솔직하게 말하면 모른 척했다.

 

그러나 그 부분들이 모여서 결국 이렇게 다시 맞닥뜨리게 됐을 때 너무나 복잡하고 큰 문제들로 다가와서 찰나지만 '모른 척하지 말 걸 그랬다, 손 댈 수가 없을 정도로 코드가 엉망인 것 같다, 코드를 갈아엎어야하나?' 라는 생각까지 들었다.

 

근데 해결해나가면서 든 생각은 "역시 안되는 건 안되는 이유가 있다" 였고 이젠 안 고쳐지는 게 아니라 이유가 있어서 안되는 거니 이유를 찾자 라는 생각이 들게 됐다!

 

확실히 에러들을 만날 때마다 힘들지만 해결하고 나면 또 하나 알게됐다는 성취감이 든다.

 

 

 

 

 

나는 mongodb를 사용하기 때문에 remove(구버전이라 이제 사용 X), deleteOne(), deleteMany() 등의 명령어를 사용해서 document를 삭제해야하는 데 삭제가 안됐다.

 

해결하고보니 두 가지의 문제점이 있었다.

 

 


 

 

문제점

 

1. 형식 (출처 : https://sehwan93.github.io/oracle/2021-06-20-mongodb2/)

 

db.{COLLECTION_NAME}.remove({query},{justOne}) 방식으로 작성해야한다.

 

내가 원래 시도했던 코드는 아래였다.

 

backend/routes/index.js

var User = require('./mongodbuser');  // DB 스키마 파일 참조

User.deleteOne();

 

왜 앞에 User라고 적었냐면 기존에 사용하던 DB 생성, find, findOne 전부 앞에 User.~라고 사용했고 잘 작동했기 때문이었다.

 

그러나 저 참고한 곳에서 명령어 형식을 보고 혹시..? 라는 생각에 아래와 같이 코드를 작성했고 에러가 생겼다. (원래는 에러도 안 생기고 삭제가 안 되기만 했음)

 

router.get('/secession', (req, res) => {
  User.find({name : req.cookies.session},(err, users) => { 
      if(!req.cookies.session || err) {  // 정보/쿠키 없거나 에러 났을 때
        res.redirect('/');
      } else {
        users.deleteOne();
        res.clearCookie('session');
        res.redirect('/'); 
      }
  })
})

 

이렇게 작성했을 때 발생한 에러는 deleteOne is not a function

 

 

2. find 사용

 

에러가 생겼을 때 구글링을 했고 deleteOne이 remove보다 최신 버전인 걸 알고 있는 상태였는데 deleteAndfindeOne ? 인가 이런 함수가 최신 버전이니 이 명령어를 쓰라고 스택에 적혀있었고 해봤으나 역시나 똑같은 오류가 생겼다.

 

알고보니 버전의 문제가 아니었고 find가 아니라 findOne을 쓰니 정상동작했다.

 

 

<성공한 코드>

router.get('/secession', (req, res) => {
  User.findOne({name : req.cookies.session},(err, users) => { 
      if(!req.cookies.session || err) {  // 정보/쿠키 없거나 에러 났을 때
        res.redirect('/');
      } else {
        users.deleteOne();
        res.clearCookie('session');
        res.redirect('/'); 
      }
  })
})

 

 

 


 

 

드디어 탈퇴 기능이 구현됐다.

 

 

 

++진짜 해결!

https://codelist.tistory.com/83 

 

 

내가 alert를 띄우려고 했던 시도들은 순서대로 아래 글들을 읽어보면 볼 수 있다.

 

1. https://codelist.tistory.com/61

 

[Vue.js, Node.js] Node.js에서 보낸 response Vue에서 처리

++해결 [Node.js, Vue.js, Ajax] ::디버깅:: Vue.js alert 넣기 여는 글 먼저 내가 어떤 기능을 구현하고 싶었는 지 간단하게 설명하자면 회원가입 시 DB를 조회한 후 아이디가 이미 존재하면 이미 존재하는

codelist.tistory.com

 

2. https://codelist.tistory.com/69

 

[Node.js, Vue.js, Ajax] ::디버깅 실패:: Vue.js alert 넣기

+ 2021.10.18 해결된 줄 알고 글을 작성했으나 알고보니 안됐다. 아래 포스팅을 보면 내가 왜 착각했는 지 나와있다. [Node.js, Vue.js, Ajax] -삽질기록- Vue.js alert 넣기 원래는 패치 내용을 따로 포스팅 할

codelist.tistory.com

 

3. https://codelist.tistory.com/73

 

[Node.js, Vue.js, Ajax] -삽질기록- Vue.js alert 넣기

원래는 패치 내용을 따로 포스팅 할 계획이 없었으나 해결이 안되다보니 기록이라도 남기려 작성하게 됐다. 그 김에 앞으로 패치 관련 포스팅은 이 카테고리에서 하도록 하겠다. 내가 올랑상점

codelist.tistory.com

 

 

 

 

그리고 아래 코드로 성공 했다.

 

전에도 alert 띄우려고 아래처럼 res.send를 시도해 본 적 있는데 alert 뜨고나면 화면이 다 날라가서 하얀 상태에서 멈춰버려서 페이지 이동이 안되니까 포기했는데 이렇게 쓰면 페이지 이동까지 된다.

res.send("<script>alert('아이디/비밀번호를 입력해주세요.');location.href='/login';</script>");

 

 

그러나 테스트를 하다가 두 번째 alert 창을 띄우려고하면 Error: Can't set headers after they are sent. 라는 오류가 생긴다.

 

이 에러는 전에도 만났던 res.send를 두 번 보냈을 때 생기는 에러다.

 

에러가 생기는 것도 문제였는데 이 에러가 생기면 서버가 바로 죽어버린다.

 

배포 시 서버가 죽어버리면 안되기 때문에 try~catch문으로 감싸줬고 서버가 죽지 않고 이어서 잘 실행이 된다.

 

if(!users || err){ //id가 DB에 없으면(즉, id가 틀렸거나 회원가입이 안되어 있거나) 
      try{
        res.send("<script>alert('아이디가 틀렸습니다.');location.href='/login';</script>");
        }catch (exception) {
          res.redirect('/login');
        }

 

제일 좋은 건 백엔드->프론트로 response를 전달해서 프론트에서 response를 받아 alert를 띄우는 거지만 지금의 나에겐 이 방식이 최선인 것 같다.

 

 

 

로컬에서는 잘 되다가 AWS로 배포하고 나니 로그인이 안되는 문제가 생겼다.

 

내 로그인 기능은 쿠키를 사용하기 때문에 쿠키를 확인해봤더니 쿠키가 생성이 안되는 문제가 있었다.

 

구글링해보니 배포 후 쿠키 생성이 안된다는 글이 꽤 있었고 읽어보니 CORS, sameSite, secure 설정 등 보안 설정을 해주라고 나와있었고 다 해봤지만 소용이 없었다.

 

도메인 연결도 해보래서 도메인도 돈 주고 샀는데 역시나 안됐다.

 

결국엔 해결했지만 늘 그렇듯이 어이 없는 이유였다.

 

 

 

 

도메인 연결하기

 

참고한 곳

 

[AWS]리액트 개인 프로젝트 배포하기(nextjs,nestjs)[7]-Cookie 문제 해결 & 도메인 설정

지난번 글에서는 Cookie 문제까지 발생한 글을 적었다. 이번 글에서는 그 Cookie를 어떻게 공유하는지 설명할 예정이다. 먼저 원인은 도메인이 다르기 때문인데 도메인을 일치시켜주려면 도메인을

2ham-s.tistory.com

 

 

마찬가지로 나도 .shop을 1년에 550원주고 샀다.

 

해결은 안됐지만...

 

 

 

해결(+12.11)

 

원인 찾는 데 거의 2주나 걸렸다.

 

원인은 늘 그렇듯이 내가 작성한 코드 때문이었다...

 

cookie로 로그인을 구현하기 때문에 보안이 너무 취약하다고 생각했던 나는 쿠키를 생성하는 부분에 Secure=true 라는 코드를 작성했다.

 

그리고 로컬에서도 아무 문제 없이 돌아가길래 신경 쓰지 않았다.

 

하지만 배포 후 쿠키가 브라우저에 저장되지 않았다.

 

그리고 구글링을 2주 가까이 하고 여러 코드를 적어보다가 우연히 읽게된 포스팅에서 브라우저 헤더 읽는 법을 알게 됐다.

 

f12-애플리케이션에서 쿠키 생기는 목록만 봤지 헤더 읽는 법은 몰랐는데 f12-네트워크를 클릭하면 메인 페이지, 서브 페이지 별로 헤더를 볼 수 있고 헤더에는 Set-Cookie라고 쿠키가 설정 된 걸 볼 수 있다.

 

 

 

여기를 살펴보니까 서브페이지인 /login에는 세션이 잘 설정되어 있었다.

 

그러니까 쿠키 생성이 안되는 게 아니라 메인 페이지에 쿠키가 전달되지 않았던 것이다.

 

그러면 어떻게 하면 쿠키를 전달할 수 있을까 하던 차에 경고 문구를 발견했다.

(저 위의 사진은 해결되고 난 후의 사진이라 경고문구가 없다)

 

 

'Secure' 속성은 있지만 보안 연결을 통해 수신되지 않았으므로 Set-Cookie 헤더를 통해 쿠키를 설정하려는 시도가 차단되었습니다.

 

이게 무슨 말인지 처음엔 몰랐다가 구글링해보고 알았다.

 

Secure 속성을 true로 해놨으면서 왜 HTTPS로 통신하지 않았지?

 

라는 뜻이다.

 

내가 산 도메인은 HTTP라서 안됐던 것이다...

(HTTPS로 변경하려면 SSL 인증서를 돈 주고 사서 설치해야 한다)

 

그래서 저 속성을 지웠더니 Secure 속성이 없는데 Samesite 속성이 지정되어있다 라는 경고로 바뀌었고 Samesite 속성도 지웠더니 쿠키가 잘 전달 된다.

 

 

 

 

결론

 

보안 때문에 설정했던 코드가 나를 12일 동안 삽질하게 만들었다.

 

그래도 헤더를 읽을 수 있게 됐다는 기쁨이 있다!

 

에러를 겪으면서 하나하나 알아가는 것 같아서 좋다.

 

 

배포 준비 첫 번째 글이다.

 

배포 전에 보안 처리를 해야하는 데 나는 어차피 GIT에 모든 코드가 올라가있기 때문에 보안 상 다 뚫려있지만 실제 상업용 사이트를 배포하는 것처럼 세팅을 해놓을 것이다.

 

그 동안은 회원가입/로그인 기능 테스트를 위해 패스워드를 암호화하지 않았지만, 이제 배포를 앞두고 보안 처리를 해야 한다.

 

먼저 패스워드를 암호화해서 저장하고 로그인 시 암호화 된 문자열을 비교해 회원 가입 여부를 판단할 수 있도록 구현할 것이다. 

 

 

참고한 사이트

 

여기는 암호화의 종류와 개념을 이해하기 쉽게 설명되어 있다.

 

(NodeJS) crypto 모듈을 사용한 암호화

안녕하세요. 이번 시간에는 crypto 모듈을 사용해서 비밀번호를 암호화하는 방법에 대해 알아보겠습니다. 예전 패스포트 강좌에서는 패스포트 기능 설명에 중점을 두었기 때문에 비밀번호는 그

www.zerocho.com

 

여기는 실제로 코드에 구현할 때 참고하기 좋다.

 

Node 내장 암호화 모듈 Crypto

Node.js를 설치하게 되면 내장 모듈중 crypto라는 모듈이 있습니다. 이 모듈을 사용하여 암호화 하는 방법...

blog.naver.com

 

두 사이트 모두에서 코드를 참고했다.

 

 

 

 

패스워드 암호화해서 DB에 저장하기

 

사실 암호화하는 코드는 참고한 사이트에서 보면 생각보다 짧아서 어렵지 않구나 하고 생각했는데 salt를 어떻게 저장해야 하는 가에서 약간 헤맸다.

 

구글링을 해보면 salt는 돌릴 때마다 값이 달라지니 로그인 기능을 구현하려면(동일 값 여부 확인) 하려면 암호화 할 당시의 salt 값이 필요하고(사용자마다 고유한 salt 값 가짐) 그러려면 어딘가에는 저장해야 한다고 나와 있었다.

 

근데 저장해야 한다면 DB가 제일 먼저 생각났는데 DB에 암호화된 패스워드랑 salt 값을 같이 저장하면 암호화한 의미가 없어지지 않나? 라는 생각이 들어서 망설여졌다.

 

게다가 DB에 저장하는 경우도 찾아봤는데 그냥 암호화 개념 설명을 편하게 하기 위해 DB에 저장하는 듯한 느낌이어서 DB에 저장해도 되나 라는 생각이 들었다.

 

그런데 다시 생각해보면 패스워드를 암호화하는 이유는 DB가 해킹 당했을 때 패스워드를 알아보지 못하게 하기 위함이고 그러면 DB에 암호화된 패스워드와 salt 값을 같이 넣어도 어떤 방식으로 암호화했는 지는 DB가 아니라 코드에 나와있기 때문에 같이 저장해도 되는 거 아닐까 라는 생각이 들었다.

 

그래서 나는 일단 DB에 salt 값을 같이 저장했다.

 

var password = request.body.password;
      crypto.randomBytes(64, (err, buf) => {
        const salt = buf.toString('base64')
        crypto.pbkdf2(password, salt, 107529, 64, 'sha512', (err, key) => {
          User.create ({  //DB 생성
            id:request.body.id,
            password:(key.toString('base64')),
            salt : salt,
            name:request.body.name,
            phone:request.body.phone,
          }); 
        });
      });

 

 

 

암호화한 패스워드 값 비교하기(로그인)

 

사용자가 입력한 id 값으로 DB에서 사용자를 찾고 DB에 같이 저장된 salt 값을 가져와서 동일한 방식으로 암호화해서 패스워드 동일 유무를 확인하는 방식으로 코드를 작성했다.

 

  User.findOne({id : req.body.id},(err, users) => {  // id를 먼저 체크해서
    if(!users || err){ //id가 DB에 없으면(즉, id가 틀렸거나 회원가입이 안되어 있거나) 
      res.redirect('/login');
    }
    else {
    var cpassword = req.body.password;  // 사용자가 입력한 패스워드를 가져와서
    crypto.randomBytes(64, (err, buf) => {
      crypto.pbkdf2(cpassword, users.salt, 107529, 64, 'sha512', (err, key) => {
        cpassword = key.toString('base64')  // DB에 저장했던 동일한 방식으로 암호화 
        
        if (users.password == cpassword) {  // 저장된 패스워드와 암호화된 입력받은 패스워드 비교
          res.cookie('session', users.name, {
          maxAge : 6000000 // 쿠키 1시간 유지
        });
      res.redirect('/');
        }
      });
    });
  }
  })

 

 

 

저장된 DB

 

 

굳이 따지자면 Node.js 카테고리는 아니지만 올랑상점 패치니까 여기에 작성한다.

 

원래는 바로 로그인, 로그아웃, 회원가입 전부 뜨게 해 놓은 상태라 로그인 안 한 상태이면 로그아웃이 뜨지 않도록 하고 싶었다.

 

해결하는 데에는 4시간 정도 걸린 것 같은데 무슨 기능을 써야 할 지 헤매느라 오래 걸렸지만 구현하는데는 오래 걸리지 않았다.

 

 

 

참고한 곳

 

Computed 속성과 Watch | Vue.js

Computed 속성과 Watch Computed 속성 템플릿 내에 표현식을 넣으면 편리하지만, 간단한 연산을 위한 부분입니다. 템플릿 안에서 너무 많은 연산을 하면 코드가 비대해지고 유지보수가 어렵습니다. 중

v3.ko.vuejs.org

 

 

 

쿠키와 document.cookie

 

ko.javascript.info

 

 

 


 

 

 

Vue에서 쿠키 가져오기

 

나는 Nodejs에서 쿠키를 생성하는데 구글링해보면 Vue에서 쿠키를 생성해서 사용하는 글이 대부분이라 여기서 조금 헤맸지만 javascript에서 쿠키 사용하는 글을 보니 document.cookie 라는 명령어를 사용했고 다행히 잘 동작했다.

 

 return document.cookie

 

예를 들면 저런 식으로.

 

 

 

 

로그인 후 목록 갱신하기

 

내 코드는 아래와 같다. (이 기능 구현에 필요한 코드만 있음. 전체 코드 X)

 

frontend/components/Header.vue

<!-- component 용. component는 재사용을 위해 사용됨 -->

<template>
  <div class="head" v-if="cookie">  <!--cookie가 true일 때 즉, 쿠키가 있다면 보여줄 목록-->
    <a v-bind:href="main.url">{{main.message}}</a>
    <h3>{{username.message}} 님!</h3>
    <router-link to="/logout">LOG OUT</router-link>
  </div>
  <div class="head" v-else>  <!--쿠키가 없다면 보여줄 목록-->
    <a v-bind:href="main.url">{{main.message}}</a>
    <router-link to="/login"> {{ login.message }} </router-link>
    <router-link to="/signup"> {{ signup.message }} </router-link>
  </div>
</template>

<script>
export default {
  name: "Header",
  data () {
    return {
      username : {  /* 쿠키에 저장된 값 디코딩해서 가져오기  */
        message :  decodeURIComponent(document.cookie),
      },
  
     computed : {  /* 여기서 쿠키를 반환하면, v-if에서 쿠키 존재하면 true로 인식  */
        cookie(){
          return document.cookie
        }
      },
};


</script>

 

 

제일 먼저 쿠키가 있을 때는 이 목록, 없을 때는 else 블록에 있는 목록을 보여주기 위해서 v-if를 써야한다.

 

v-if는 true/false로 판별해서 해당하는 값을 보여주는데 나처럼 값(쿠키)의 유무를 이용해서 띄울 수 있다.

 

여기서 제일 중요한 게 computed 다.

 

참고한 사이트에 들어가서보면 설명이 잘 되어 있는데, 내가 막혔던 부분이 로그인 후 없던 쿠키가 생겼을 때 갱신이 되면서 회원명, 로그아웃 버튼이 보이도록하는 부분이었는데 이 명령어가 해결해줬다.

 

원래는 쿠키가 생겨도 목록이 갱신되지 않아서 막혀있던 차에 저 명령어를 사용하니 로그인 후에 잘 갱신된다.

 

주의할 점 : 반드시 computed는 return {}밖에 써줘야 한다. 이거 때문에 1시간이나 붙잡고 있었다.

 

 

 return 부분에 디코딩이 있는 이유는 내 코드는 쿠키에 사용자명을 저장하고 그 사용자 명을 가져와서 000이라는 사람이 로그인 되었다고 보여주고 싶었는데 디코딩을 안하면 쿠키값을 그대로 가져오기 때문에 숫자%알파벳으로 보인다.

 

그리고 사실 지금 저 코드 상태로는 session=000 님! 이라고 뜨기 때문에 String.slice()를 써서 session= 부분을 잘라야하는데 일단 잠시 미뤘다. (백엔드에 할 게 많아서)

 

 

로그인 전

 

로그인 후