과거 프로그래밍 자료들/프로젝트

프로젝트 수정 3 - node.js로만 구성된 프로젝트에 리액트 추가(서버 항목 : 로그인/인증2)

평부 2022. 5. 13. 17:13

* 현재 진행상황

- 프로젝트에 CRUD만 만든 상황

- fetch로 로그인 했으나 auth로 로그인 및 react로 로그인 화면을 만드는 것이 중요하다고 판단

- node.js 화면단을 ejs로 만들었으나 react에서 js로 만들 예정

- 참고 강의 : 따라하면서 배우는 노드, 리액트(로그인 부분)

 

 

*참고할 글(Cookie VS Session VS Token(JWT) 차이)

(https://devyuseon.github.io/about%20dev/cookie-session-token/)

 

 

[인증 기능 만들기 - server 부분]

1. index.js에 router 만들기 

2. auth.ctrl.js에서 기능만들기

 - 인증이 성공한다면 나올 정보들

3. models/User.js 인증을 하는 메서드 만들기

-  findByToken: 토큰 찾기

3. models/user.js -> middleware/auth/auth.js -> auth.ctrl에 들어갈 기능 추가하기

-  findByToken: 토큰 찾기 

- generateToken : 로그인 부분에서 설명 

4. postman에서 확인하기

 

 

1. index.js에 router 만들기  - [routes/auth/index.js]

 

const express = require("express");
const router = express.Router();
const authCtrl = require("./auth.ctrl");
const { auth } = require("../../middleware/auth/auth"); //인증기능을 하는 미들웨어

router.post("/api/user/register", authCtrl.process.register);
router.post("/api/user/login", authCtrl.process.login);
router.get("/api/user/auth", auth, authCtrl.process.auth);

module.exports = router;

 

 

2. auth.ctrl.js에서 기능만들기

 - 인증이 성공한다면 나올 정보들

 

"use strict";
const { User } = require("../../models/home/user");

const process = {
  register: (req, res) => {
    //회원가입
  },

  login: (req, res) => {
    //로그인
        //비밀번호가 맞다면 token 생성
        user.generateToken((err, user) => {
          if (err) return res.status(400).send(err);

          //토큰을 저장한다 어디에? 1. 쿠키, 2. 로컬스토리지
          res
            .cookie("x_auth", user.token)
            .status(200)
            .json({ loginSuccess: true, userId: user._id });
      });
    });
  },
  
  //인증 부분 
  auth: (req, res) => {
    res.status(200).json({
      _id: req.user._id,
      isAdmin: req.user.role === 0 ? false : true, //0이면 false 0 이외의 값 true
      isAuth: true,
      email: req.user.email,
      name: req.user.name,
      lastname: req.user.lastname,
      role: req.user.role,
      image: req.user.image,
    });
  },
};

module.exports = {
  process,
};

 

 

3. models/User.js 인증을 하는 메서드 만들기

-  findByToken: 토큰 찾기

참고 : (https://velog.io/@rosewwross/%EC%95%94%ED%98%B8%ED%99%94%EC%99%80-%ED%86%A0%ED%81%B0%EC%9D%98-%EC%9D%B4%ED%95%B4)

- 토큰 가져오기 : auth.ctrl.js 의 res.cookie("x_auth")

- 토큰을 복호화 : 암호화(특정 알고리즘을 이용해 암호화된 형태로 변형)한 것을 역행하는 과정, 암호화된 정보를 다시 읽을 수 있음 

 

const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const saltRounds = 10;
const jwt = require("jsonwebtoken");

const userSchema = new mongoose.Schema({
  //스키마
});

//user.save 전에 작동
userSchema.pre("save", function (next) {});

userSchema.methods.comparePassword = function (plainPassword, cb) {
//비밀번호 맞는지 확인
};

userSchema.methods.generateToken = function (cb) {
  var user = this;

  //jsonwebtoken을 이용해 token 생성
  var token = jwt.sign(user._id.toHexString(), "secretToken");

  user.token = token;
  user.save(function (err, user) {
    if (err) return cb(err);
    cb(null, user);
  });
};

userSchema.statics.findByToken = function (token, cb) {
  var user = this;
  //토큰을 복호화 하는 작업
  jwt.verify(token, "secretToken", function (err, decoded) {
    //유저 아이디를 이용해서 유저를 찾은 다음에
    //클라이언트에서 가져온 token과 DB에 보관된 토큰이 일치하는지 확인
    user.findOne({ _id: decoded, token: token }, function (err, user) {
      if (err) return cb(err);
      cb(null, user);
    });
  });
};

const User = mongoose.model("User", userSchema);

module.exports = { User };

 

* schema.statics를 사용하는 이유

* 출처 : (https://mongoosejs.com/docs/guide.html)

 

 

* 토큰 복호화 관련 작업

* 출처 : (https://www.npmjs.com/package/jsonwebtoken)

 

 

4. models/User.js -> middleware/auth/auth.js -> auth.ctrl에 들어갈 기능 추가하기 - [auth.js]

 

const { User } = require("../../models/home/user");

let auth = (req, res, next) => {
  //인증 처리하는 곳

  //클라이언트 쿠키에서 토큰 가져옴(x_auth : auth.ctrl.js에서 토큰 생성 시 만든 이름)
  let token = req.cookies.x_auth;

  //토큰을 복호화 한 후 유저를 찾는다(findByToken <- user.js)
  user.findByToken(token, (err, user) => {
    if (err) throw err;
    if (!user) return res.json({ isAuth: false, error: true });
  });

  req.token = token; //auth.ctrl.js에서 사용할 수 있게 함
  req.user = user; //auth.ctrl.js에서 사용할 수 있게 함
  next(); //이 부분이 없으면 auth 미들웨어에 계속 갇혀있게 됨
  //
};

module.exports = { auth };

 

* [models/User.js] 전체 코드

 

const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const saltRounds = 10;
const jwt = require("jsonwebtoken");

const userSchema = new mongoose.Schema({
  name: {
    type: String,
    maxlength: 50,
  },
  email: {
    type: String,
    trim: true,
    unique: 1,
  },
  password: {
    type: String,
    minlength: 5,
  },
  lastname: {
    type: String,
    maxlength: 50,
  },
  role: {
    type: Number,
    default: 0,
  },
  image: String,
  token: {
    type: String,
  },
  tokenExp: {
    type: Number,
  },
});

//user.save 전에 작동
userSchema.pre("save", function (next) {
  //위의 user 가져오기
  var user = this;
  //비밀번호 바꿀 때 조건
  if (user.isModified("password")) {
    //비밀번호 암호화
    bcrypt.genSalt(saltRounds, function (err, salt) {
      if (err) return next(err);
      bcrypt.hash(user.password, salt, function (err, hash) {
        if (err) return next(err);
        user.password = hash;
        next();
      });
    });
  } else {
    //비밀번호가 바뀌지 않을 때 next()가 바로 시작됨
    next();
  }
});

userSchema.methods.comparePassword = function (plainPassword, cb) {
  //위의 user 가져오기
  var user = this;
  //plainPassword : 암호화 전 비밀번호 cb : 암호화된 비밀번호
  bcrypt.compare(plainPassword, user.password, function (err, isMatch) {
    if (err) return cb(err);
    cb(null, isMatch);
  });
};

userSchema.methods.generateToken = function (cb) {
  var user = this;

  //jsonwebtoken을 이용해 token 생성
  var token = jwt.sign(user._id.toHexString(), "secretToken");

  user.token = token;
  user.save(function (err, user) {
    if (err) return cb(err);
    cb(null, user);
  });
};
userSchema.statics.findByToken = function (token, cb) {
  var user = this;
  //토큰을 decode 한다.
  jwt.verify(token, "secretToken", function (err, decoded) {
    //유저 아이디를 이용해서 유저를 찾은 다음에
    //클라이언트에서 가져온 token과 DB에 보관된 토큰이 일치하는지 확인
    user.findOne({ _id: decoded, token: token }, function (err, user) {
      if (err) return cb(err);
      cb(null, user);
    });
  });
};

const User = mongoose.model("User", userSchema);

module.exports = { User };

 

4. postman에서 확인하기