boiler-plate

강정우·2022년 10월 18일
0

node.js

목록 보기
2/3
post-thumbnail
post-custom-banner

1. 회원가입 라우터

  • register router를 만들어보자

body-parser

  • 우선 body-parser를 가져온다.
const bodyParser = require('body-parser')
  • 그리고 몇가지 옵션을 추가해준다.
    각각 application/x-www-form-urlencoded 타입과 application/json 타입을 분석해서 가져올 수 있도록 해주는 코드이다.
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
  • 그리고 이 요청을 받을 객체가 필요하다 바로 모델의 User에 정의를 해두었고 이것또한 import해야한다.
const {User} = require("./models/User");
  • 자 준비가 다 되었으면 이제 등록라우터를 만들차례이다.

register router

app.post('/register', (req, res) => {
  const user = new User(req.body)
  user.save((err, userInfo)=>{
    if(err)return res.json({ success: false, err})
    return res.status(200).json({
      success:true
    })
  })
})
  • 정보들을 DB에 넣으려면 json 형태로 되어있는 req.body를 그대로 넣으면 되는데 이를 가능하게 해주는게 body-parser가 해준다.
  • save() 는 몽고 DB 함수이다.

2. 비밀설정 정보관리

  • 자 위에 mongoose객체로 connect 함수를 보면 개인정보들이 들어가있는것을 확인할 수 있다. 이렇게 되면 github에 올릴 때 보안이 하나도 없는 상태로 올라가게 된다. 그래서 따로 덜어내서 gitignore에 넣어주어야 한다.
  • 그래서 따로 관리를 해주어야 하는데 여기서 또 local과 deploy 후 production 의 차이점이 있다. 배포중에는 헤로쿠 에서 민감정보를 control하고 local 환경에서는 폴더를 하나 만들어서 거기서 관리를 해주어야 한다.

config 폴더를 만들어 민감정보를 따로 관리해주고 gitignore에 등록하여 옆에 초록 점을 없앤 상태이다.

key.js

if(process.env.NODE_ENV === 'production'){
    module.exports = require('./prod');
} else {
    module.exports = require('./dev');
}
  • process.env.NODE_ENV 가 환경변수라서 우리가 어디서 접속했는가를 문자열로 반환해준다. 그래서 Local 환경이라면 development라고 반환된고 Deploy 후 라면 production으로 반환한다.

mongoose.connection 함수

const config = require('./config/key')

mongoose.connect(config.mongoURI,{
}).then(()=> console.log('mongoDB is connected...'))
    .catch(err=>console.log(err))
  • config 객채 생성 후 mongoURI 필드값을 넣어준 상태이다.

Bcrypt로 비밀번호를 암호화 하기

  • 2명의 데이터를 임의로 넣었다. 다행히 unique 속성값도 잘 작동이 된다.
  • 하지만 현재 데이터 베이스에 저장된 pw를 보면 너무 안전하지 않다. 그래서 Bcrypt를 이용하여 pw를 암호화하여 DB에 저장할 것이다.
  • 이때 암호화의 시기는 register라우터에서 user 객체를 불러온 후 save() 전에 실시를 해야한다.

salt 생성

  • pw를 해시하기위해 사용되는것이 "SALT"이다.
    숫자로 지정되면 지정된 라운드 수로 "SALT"가 생성되어 사용된다.
  • salt를 이용하여 hash(암호화된) pw 만들기 자세한 방법들은 npm의 Usage 참조
  1. salt 생성 함수 사용 genSalt()
  2. saltRounds에 문자열 수 지정하기
  3. 이 saltRounds를 이용하여 문자열 암호화하기

index.js

const bcrypt = require('bcrypt');
const saltRounds = 10;

User.js

// 몽구스 method pre => ~전에
userSchema.pre('save', function(next){
    var user = this;
    // 뭐 할때마다 pw를 암호화하는 것이 아닌 pw를 바꾼다고 할 때만 pw를 암호화해주어야한다.
    if(user.isModified('password')){
        // 비밀번호를 암호화 시킨다.
        bcrypt.genSalt(saltRounds, function(err, salt) {
            if(err) return next(err)
            bcrypt.hash(user.password, salt, function(err, hash) {
                // Store hash in your password DB.
                if(err) return next(err)
                user.password = hash
                next()
            });
        });
    } else {
        // 이 else 구문이 있어야 if 문이 아닐때 이더라도 빠져나올 수 있다.
        next()
    }
})
  • isModified('name') : 사용자로부터 요청이 name인지 확인.
  • bcrypt.genSalt : salt 생성
  • bcrpyt.hash : hash 함수에 해당 필드값과 salt를 집어넣어서 암호화

3. login route 만들기

  • router 중 어려운 편에 속함

1) DB에서 요청한 E-mail찾기

app.post('/login', (req, res) => {
  User.findOne({email : req.body.email}, (err, user) => {
    if(!user){
      return res.json({
        loginSuccess : false,
        message : "존재하지 않는 e-mail입니다."
      })
    }
  }
})
  • 몽고DB에서 제공하는 findOne 함수를 이용하여 e-mail이 존재하는지 찾는다.

2) DB에서 요청한 E-mail이 있다면 pw가 같은지 확인

index.js

  • 이제 comparePassword함수를 만들어보자.
app.post('/login', (req, res) => {
  User.findOne({email : req.body.email}, (err, user) => {
    if(!user){
      return res.json({
        loginSuccess : false,
        message : "존재하지 않는 e-mail입니다."
      })
    }
    // 새로 추가된 함수
    user.comparePassword(req.body.password, (err, isMatch)=>{
      if(!isMatch) return res.json({ loginSuccess : false, message : "비번이 틀렸음" })
    })
  }
})
  • comparePassword 함수에는 2가지의 인자가 필요한데 한개는 사용자가 입력한 값 나머지 하나는 이 변수를 토대로 처리를 해줄 콜백함수이다.

User.js

  • comparePassword 구현
userSchema.methods.comparePassword = function(plainPassword, cb){
    bcrypt.compare(plainPassword, this.password, function(err, isMatch){
        if(err) return cb(err)
        cb(null, isMatch)
    })
}
  • 이때 암호화된 pw를 복호화할 수 없기에 사용자가 입력한 값을 다시 암호화하여 DB에 저장된 pw와 일차하는지 확인해야한다.
  • compare의 함수가 궁금하다면 usage 참조
npm install jsonwebtoken --save
npm install cookie-parser --save
  • pw가 일치하다면 Token 생성한다. 그리고 이 토큰을 저장한다 어디에? user에 저장되어있는 것을 어느 scope에 저장시켜야함 일단 쿠키에 저장해보겠다.

index.js

app.post('/login', (req, res) => {
  User.findOne({email : req.body.email}, (err, user) => {
    if(!user){
      return res.json({
        loginSuccess : false,
        message : "존재하지 않는 e-mail입니다."
      })
    }
    user.comparePassword(req.body.password, (err, isMatch)=>{
      if(!isMatch) return res.json({ loginSuccess : false, message : "비번이 틀렸음" })
    })
    // 새로 추가된 함수
    user.generateToken((err, user)=>{
      if(err) return res.status(400).send(err)
      res.cookie("cookie_name", user.token)
      .status(200)
      .json({loginSucess : true, userId : user.id})
    })
  }
})

User.js

userSchema.methods.generateToken = function(cb) {
    var user = this;
    var token = jwt.sign(user._id.toHexString(), 'secretToken');
    user.token = token
    user.save(function(err, user){
        if(err) return cb(err)
        cb(null, user)
    })
    // user.save 라는 method가 어디서 나온거임?
}
  • 참고로 es5 문법을 따르고 있다.
  • 사이트를 참조하여 토큰 생성

토큰 동작 원리

  • sing함수에 DB의 id값과 내가 지정한 String값을 더해서 토큰을 생성하고 나중에 이 토큰을 해석할 때 지정한 String값을 통하여 id값을 찾는다.

JWT란

⚖️ Json Web Token

  • RFC 7519(버젼 이름임) 웹 표준으로 지정이 되어있고 JSON 객체를 사용해서 토큰 자체에 정보들을 저장하고 있는 Web Token 이라고 정의할 수 있다.

장점

  • JWT 이용방식이 간편하고 쉽게 적용할 수 있다는 장점이 있다.
  • 중앙의 인증서버, 데이터 스토어에 대한 의존성이 없다.
  • 따라서 시스템 수평확장에 유리하다.
  • Base64 URL Safe Encoding > URL, Cookie, Header에서 모두 사용이 가능하다

단점

  • payload의 정보가 많아지면 네트워크 사용량 증가, 데이터 설계 고려 필요하다.
  • 토큰이 클라이언트에 저장, 서버에서 클라이언트의 토큰을 조작할 수 없다.

🛠️ 구성

Header, payload, Signature 3개의 부분으로 구성되어있다.

  • Signature를 해싱하기위한 알고리즘 정보들이 담겨있고

payload

  • 서버와 클라이언트가 주고받는, 시스템에서 실제로 사용될 정보에 대한 내용들을 담고있다.

Signature

  • 토큰의 유효성 검증을 위한 문자열이다.
profile
智(지)! 德(덕)! 體(체)!
post-custom-banner

0개의 댓글