프로젝트 수정 3 - node.js로만 구성된 프로젝트에 리액트 추가(서버 항목 : 로그인/인증2)
* 현재 진행상황
- 프로젝트에 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: 토큰 찾기
- 토큰 가져오기 : 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에서 확인하기