프로젝트 1 - Auth
⭐프로젝트 1 - Auth
📕Auth
Auth 라우트를 왜 만들까??
우리가 특정 사이트를 간다고 했을 때 로그인이 된 유저들만 이용할 수 있고,
다른페이지는 누가나 사용할 수 있고 이런 차이점이 존재한다
그런것을 체크해주기 위해서 Auth 기능을 만드는 것이다
어떻게 구현을 하는지 설명하겠다
현재 우리는 토큰이 서버의 user 정보에 저장되어있고,
클라이언트의 쿠키에도 저장되어 있다
만약 사용자가 A사이트에서 B사이트로 넘어갈 때
이 사용자가 B사이트의 권한이 있는지
확인하기 위하여 클라이언트의 쿠키를 서버로 보낸다
쿠키 내에 토큰이 인코드 되어있는 상태인데 서버에서 받은 토큰을 디코드한다
지난 포스트에서 'secretToken'을 넣으면 user id가 나온다고 했었다
그럼 그 user id를 이용해서 그 user id의 DB에 토큰이 있다면 인증이 되는것이다
순서대로 설명하자면
- 쿠키에 저장된 토큰을 서버로 가져와서 복호화를 한다
- 복호화를 하면 user id가 나온다
- 그 user id로 DB에서 유저를 찾은 후 쿠키에서 받은 토큰이 있는지 확인한다
이렇게 진행된다
📘만들기
우리가 지금까지 라우터를 만들 때 그냥 바로 login, signup 이렇게 작성했는데
나중에 라우터가 많아지면 어느것과 관련된 정보인지 헷갈릴 수 있다
그래서 지금까지 우리가 만든 라우터는 유저와 관련된 라우터이기때문에
/api/users를 추가하도록 하겠다
그리고 auth라는 미들웨어를 사용할 것이기 때문에 middleware 폴더를 만들자
이후 폴더 안에 auth.js라는 파일을 만든뒤 아래의 코드를 작성한다
let auth = (req, res, next) => {
//인증 처리 하는곳
}
module.exports = { auth }
그리고 index.js에서 사용할 수 있게끔 상단에
const { auth } = require('./middleware/auth')
를 추가해야한다
이제 auth.js 파일에서 인증처리를 해보자
클라이언트에서 쿠키 가져오기
지난번에 사용한 쿠키파서를 사용하면 된다
쿠키를 넣을 때 x_auth라는 이름으로 넣었다
let token = req.cookies.x_auth
이렇게 하면 토큰을 쿠키에서 가져온다
토큰 복호화(decode)
유저 모델이 필요해서 상단에 유저 모델을 추가하도록 하자
const { User } = require('../models/User')
토큰을 만드는 메소드를 user.js에서 만든 것 처럼
토큰을 이용해서 찾는 메소드도 user.js에서 만들어야 한다
userSchema.statics.findByToken = function ( token, cb ){
let user = this
jwt.verify(token, 'secretToken', function(err, decoded){
//userid를 이용해서 확인하기
})
}
jwt.verify 는 기본적으로 jwt에서 제공하는 메소드이다
사이트에 가면 자세한 설명이 나온다
간단하게 설명하면 우리가 토큰을 만드는 방식을 전에 설명했지만 다시 말하자면
user id + 문자열 -> 토큰 이다
그래서 verify할 때 토큰을 넣고, 우리가 정한 문자열을 넣은 뒤에
콜백함수로 에러가 났을땐 에러, 제대로 되면 디코드한 값을 리턴하는 방식이다
userid로 유저찾고 인증하기
디코드한 값으로 현재 db에 있는 user에서 찾는 방법이다
userSchema.statics.findByToken = function ( token, cb ){
let user = this
jwt.verify(token, 'secretToken', function(err, decoded){
user.findOne({ "_id": decoded, "token": token }, function (err, user){
if(err) return cb(err)
cb(null, user)
})
})
}
user에서 findOne 이라는 몽고db 메소드를 활용하여 유저를 찾는 것이다
지난번에 한 것과 비슷해서 이해가 될 것이다
이렇게 메소드를 다 만들었으면 auth.js로 가서 사용하도록 하자
const { User } = require('../models/User')
let auth = (req, res, next) => {
let token = req.cookies.x_auth
User.findByToken(token, (err, user) => {
if(err) throw err
if(!user) return res.json({ isAuth: false, error: true })
req.token = token
req.user = user
next()
})
}
module.exports = { auth }
이렇게 findByToken 메소드를 활용한다
next()를 넣은 이유는 미들웨어이기 때문에 넘어가는게 없으면 계속 갇히게된다
index.js에서 req.token, req.user 정보를 받을 수 있다
app.get('api/users/auth', auth, (req, res) => {
//미들웨어를 통과해 여기까지 왔다는 것은 Auth가 true 라는 의미
})
여기까지 왔다는 것은 Auth가 true라는 것이다
이것을 클라이언트에게 보여주기위해 조금의 코드를 추가하도록 하자
사용자의 정보를 띄워줄 것이다
여기서 role에 0이 있으면 일반사용자이고,
0이 아닌 다른 수가 있다면 관리자로 취급한다
app.get('/api/users/auth', auth, (req, res) => {
//미들웨어를 통과해 여기까지 왔다는 것은 Auth가 true 라는 의미
res.status(200).json({
_id: req.user._id,
isAdmin: req.user.role === 0 ? false : true,
isAuth: true,
email: req.user.email,
name: req.user.name,
role: req.user.role,
image: req.user.image
})
})
이렇게 코드를 작성해 주었다
이후 포스트맨을 사용하여 확인을 해보자
이렇게 잘 나온것을 확인할 수 있다
📗코드
auth.js
const { User } = require('../models/User')
let auth = (req, res, next) => {
let token = req.cookies.x_auth
User.findByToken(token, (err, user) => {
if(err) throw err
if(!user) return res.json({ isAuth: false, error: true })
req.token = token
req.user = user
next()
})
}
module.exports = { auth }
user.js
const mongoose = require('mongoose')
const bcrypt = require('bcrypt')
const saltRounds = 10
const jwt = require('jsonwebtoken')
const userSchema = mongoose.Schema({
name: {
type: String,
maxlength: 50
},
email: {
type: String,
trim: true,
unique: 1
},
password: {
type: String,
minlength: 5
},
role: {
type: Number,
default: 0
},
image: String,
token: {
type: String
},
tokenExp:{
type: Number
}
})
userSchema.pre('save', function(next){
let 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()
}
})
userSchema.methods.comparePassword = function(plainPassword, cb) {
bcrypt.compare(plainPassword, this.password, function(err, isMatch) {
if(err) return cb(err)
cb(null, isMatch)
})
}
userSchema.methods.generateToken = function(cb){
let user = this
let 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 ){
let user = this
jwt.verify(token, 'secretToken', function(err, decoded){
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 }
index.js
const express = require('express')
const app = express()
const port = 3000
const mongoose = require('mongoose')
const bodyParser = require('body-parser')
const config = require('./config/key')
const cookieParser = require('cookie-parser')
const { auth } = require('./middleware/auth')
const { User } = require("./models/User")
app.use(bodyParser.urlencoded({extended: true}))
app.use(bodyParser.json())
app.use(cookieParser())
mongoose.connect(config.mongoURI
).then( () => console.log('MongoDB Connected'))
.catch(err => console.log(err))
app.get('/', (req, res) => {
res.send('Hello World! BooKi')
})
app.post('/api/users/signup', (req, res) => {
//회원 가입 할 때 작성한 정보들을 가져와 DB에 넣어준다
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
})
})
})
app.post('/api/users/login', (req, res) => {
User.findOne({ email: req.body.email }, (err, user) => {
if(!user) {
return res.json({
loginSuccess: false,
message: "해당 이메일에 해당하는 유저가 없습니다."
})
}
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("x_auth", user.token)
.status(200)
.json({
loginSuccess: true,
userId: user._id
})
})
})
})
})
app.get('/api/users/auth', auth, (req, res) => {
//미들웨어를 통과해 여기까지 왔다는 것은 Auth가 true 라는 의미
res.status(200).json({
_id: req.user._id,
isAdmin: req.user.role === 0 ? false : true,
isAuth: true,
email: req.user.email,
name: req.user.name,
role: req.user.role,
image: req.user.image
})
})
app.listen(port, () => {
console.log(`http://localhost:${port}/`)
})
Author And Source
이 문제에 관하여(프로젝트 1 - Auth), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@qnrl3442/프로젝트-1-Auth저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)