From 678910ddbdbdf4c69520450615c9b78e0e28b2b2 Mon Sep 17 00:00:00 2001 From: leehj050211 Date: Sun, 20 Mar 2022 20:46:36 +0900 Subject: [PATCH] =?UTF-8?q?Fix:=20REST=20API=20=EC=95=84=ED=82=A4=ED=85=8D?= =?UTF-8?q?=EC=B2=98=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/account/account.repository.js | 15 --- src/api/account/account.service.js | 3 +- src/api/account/token.repository.ts | 36 +++++++ src/api/api.controller.js | 23 ++--- src/controller.js | 20 +--- src/main.js | 10 +- src/util/jwt.ts | 137 +++++++++++++++++--------- src/view/board.controller.js | 70 +++---------- src/view/view.controller.js | 128 ++++-------------------- 9 files changed, 185 insertions(+), 257 deletions(-) create mode 100644 src/api/account/token.repository.ts diff --git a/src/api/account/account.repository.js b/src/api/account/account.repository.js index 41fc3f8..7d0541a 100644 --- a/src/api/account/account.repository.js +++ b/src/api/account/account.repository.js @@ -150,20 +150,6 @@ const updatePWByCode = async (memberCode, memberPw) => { return salt; } -const getToken = async (token) => { - const getTokenQuery="SELECT * FROM `tokens` WHERE `token`=? AND `valid`=1"; - try{ - const [rows] = await pool.query(getTokenQuery, [token]); - if(rows.length) - return rows[0]; - else - return null; - }catch(err){ - console.error(err); - throw new InternalServerException(); - } -} - module.exports = { getMemberById, getMemberByCode, @@ -174,5 +160,4 @@ module.exports = { getStudentInfoByCode, updateCodeAvailable, updatePWByCode, - getToken } \ No newline at end of file diff --git a/src/api/account/account.service.js b/src/api/account/account.service.js index 4a9b240..4901440 100644 --- a/src/api/account/account.service.js +++ b/src/api/account/account.service.js @@ -1,5 +1,6 @@ const { NotFoundException, BadRequestException, UnAuthorizedException, ConflictException, InternalServerException } = require('../../util/exceptions'); const repository = require('./account.repository'); +const tokenRepository = require('./token.repository'); const jwt = require('../../util/jwt'); const crypto = require('crypto'); const sharp = require('sharp'); @@ -258,7 +259,7 @@ const token = async (refreshToken) => { throw new UnAuthorizedException(); } // db에서 리프레시 토큰 사용이 가능한지 확인 - const tokenInfo = await repository.getToken(result.token); + const tokenInfo = await tokenRepository.getToken(result.token); // 리프레시 토큰이 db에서 사용불가 되었으면 if(tokenInfo === null){ throw new UnAuthorizedException(); diff --git a/src/api/account/token.repository.ts b/src/api/account/token.repository.ts new file mode 100644 index 0000000..488ae25 --- /dev/null +++ b/src/api/account/token.repository.ts @@ -0,0 +1,36 @@ +import { InternalServerException } from '../../util/exceptions'; +const pool = require('../../util/db'); + +const getToken = async ( + token: string, +) => { + const getTokenQuery="SELECT * FROM `tokens` WHERE `token`=? AND `valid`=1"; + try{ + const [rows] = await pool.query(getTokenQuery, [token]); + if(rows.length) + return rows[0]; + else + return null; + }catch(err){ + console.error(err); + throw new InternalServerException(); + } +} + +const insertToken = async ( + token: string, + memberCode: number +) => { + const insertTokenQuery="INSERT INTO `tokens` VALUES(?, 1, ?, now())"; + try{ + await pool.query(insertTokenQuery, [token, memberCode]); + }catch(err){ + console.error(err); + throw new InternalServerException(); + } +} + +export { + getToken, + insertToken +} \ No newline at end of file diff --git a/src/api/api.controller.js b/src/api/api.controller.js index 270f96b..8c83ae0 100644 --- a/src/api/api.controller.js +++ b/src/api/api.controller.js @@ -1,37 +1,33 @@ -const express = require('express') -const router = express.Router() -const jwt = require('../util/jwt') -const multer = require('multer') +const express = require('express'); +const router = express.Router(); +const jwt = require('../util/jwt'); +const multer = require('multer'); const loginCheck = (req, res, next) => { const jwtValue = jwt.check(req.cookies.token); if(!jwtValue.isLogin){ - return res.send(JSON.stringify(jwtValue.msg)); + next(new UnAuthorizedException()); } next(); } const imageUpload = multer({ storage:multer.diskStorage({ destination:(req, file, cb) => { - const jwtValue = jwt.check(req.cookies.token); - if(!jwtValue.isLogin) return; - cb(null, 'public/resource/board/upload_images/') + cb(null, 'public/resource/board/upload_images/'); }, filename:(req, file, cb) => { - cb(null, Date.now()+'.'+file.originalname.split('.')[file.originalname.split('.').length-1]) + cb(null, Date.now()+'.'+file.originalname.split('.')[file.originalname.split('.').length-1]); } }) }) const profileUpload = multer({ storage:multer.diskStorage({ destination:(req, file, cb) => { - const jwtValue = jwt.check(req.cookies.token); - if(!jwtValue.isLogin) return; - cb(null, 'public/resource/member/profile_images/') + cb(null, 'public/resource/member/profile_images/'); }, filename:(req, file, cb) => { const jwtValue = jwt.check(req.cookies.token); - cb(null, 'temp-profile_'+jwtValue.memberCode+'.'+file.originalname.split('.')[file.originalname.split('.').length-1]) + cb(null, 'temp-profile_'+jwtValue.memberCode+'.'+file.originalname.split('.')[file.originalname.split('.').length-1]); } }) }) @@ -52,6 +48,7 @@ const commentController = require('./board/comment.controller') const likeController = require('./board/like.controller') const imageUploadController = require('./board/imageUpload.controller') const emoticonController = require('./board/emoticon.controller') +const { UnAuthorizedException } = require('../util/exceptions') router.post('/account/login', accountController.login) router.delete('/account/logout', accountController.logout) diff --git a/src/controller.js b/src/controller.js index becdf7d..8c4a7be 100644 --- a/src/controller.js +++ b/src/controller.js @@ -1,32 +1,18 @@ const express = require('express'); const router = express.Router(); -const jwt = require('./util/jwt'); const apiRouter = require('./api/api.controller'); const viewRouter = require('./view/view.controller'); +// 업데이트 확인 url 하위호환 const versionController = require('./api/version/version.controller'); -router.post('/database', versionController.getVersionLegacy);// 업데이트 확인 url 하위호환 - +router.post('/database', versionController.getVersionLegacy); router.use('/api', apiRouter); router.use('/', viewRouter); - router.use((req, res) => { - const jwtValue = jwt.check(req.cookies.token); - res.status(404).render('404', { - member:{ - isLogin:jwtValue.isLogin, - code:jwtValue.memberCode, - id:jwtValue.memberId, - nickname:jwtValue.memberNickname, - level:jwtValue.memberLevel, - grade:jwtValue.grade, - classNo:jwtValue.classNo, - studentNo:jwtValue.studentNo, - } - }) + res.status(404).render('404'); }) module.exports = router; \ No newline at end of file diff --git a/src/main.js b/src/main.js index 6012d0b..b8bd407 100644 --- a/src/main.js +++ b/src/main.js @@ -34,10 +34,16 @@ app.use('/', controller); app.use(function (err, req, res, next) { if(err.httpCode){ - res.status(err.httpCode).send(JSON.stringify({'statusCode':err.httpCode,'message':err.message})) + res.status(err.httpCode).send(JSON.stringify({ + statusCode:err.httpCode, + message:err.message + })); }else{ console.error(err); - res.status(500).send('Internal Server Error'); + res.status(500).send(JSON.stringify({ + statusCode:500, + message:'Internal Server Error' + })); } }); diff --git a/src/util/jwt.ts b/src/util/jwt.ts index fd35711..12ce47a 100644 --- a/src/util/jwt.ts +++ b/src/util/jwt.ts @@ -1,8 +1,9 @@ import express from "express"; +import { UnAuthorizedException } from "./exceptions"; const jwt = require('jsonwebtoken'); const crypto = require('crypto'); -const pool = require('./db'); const accountRepository = require('../api/account/account.repository'); +const tokenRepository = require('../api/account/token.repository'); const secretKey = process.env.SECRET_KEY; @@ -25,8 +26,9 @@ const login = async ( grade:number, classNo:number, studentNo:number - }, expire:string) => { - const token = crypto.randomBytes(64).toString('hex') + }, expire:string +) => { + const token = crypto.randomBytes(64).toString('hex'); const result = { token: jwt.sign(payload, secretKey, { algorithm:'HS256', @@ -37,15 +39,10 @@ const login = async ( expiresIn:'60d' }), }; - const insertTokenQuery="INSERT INTO `tokens` VALUES(?, 1, ?, now())" - try{ - await pool.query(insertTokenQuery, [token, payload.memberCode]) - }catch(err){ - console.error(err) - return null; - } + await tokenRepository.insertToken(token, payload.memberCode); return result; } + const verify = (token:String) => { let decoded; try { @@ -61,30 +58,25 @@ const verify = (token:String) => { } return decoded; } + const check = (token:String|undefined) => { if(token){ const result = verify(token); if(result=='EXPIRED' || result=='INVALID'){ return { - isLogin:false, - msg:{ - status:4,subStatus:1 - } + isLogin:false }; }else{ if(!result.isLogin){ - result.msg={ - status:4,subStatus:1 - } + return { + isLogin:false + }; } - return result + return result; } }else{ return { - isLogin:false, - msg:{ - status:4,subStatus:1 - } + isLogin:false }; } } @@ -100,6 +92,7 @@ const refreshToken = async (req:express.Request, res:express.Response, next:expr return next(); } } + const result = verify(req.cookies.refreshToken); // 리프레시 토큰이 유효하지 않으면 무시하고 넘어감 if(result=='INVALID'){ @@ -107,45 +100,96 @@ const refreshToken = async (req:express.Request, res:express.Response, next:expr domain:'.bssm.kro.kr', path:'/', }); + res.clearCookie('refreshToken', { + domain:'bssm.kro.kr', + path:'/', + }); + res.clearCookie('token', { + domain:'.bssm.kro.kr', + path:'/', + }); + res.clearCookie('token', { + domain:'bssm.kro.kr', + path:'/', + }); return next(); } + // 리프레시 토큰이 만료되었으면 로그인을 요청 if(result=='EXPIRED'){ res.clearCookie('refreshToken', { domain:'.bssm.kro.kr', path:'/', }); - return res.send(JSON.stringify({status:4,subStatus:5})); + res.clearCookie('refreshToken', { + domain:'bssm.kro.kr', + path:'/', + }); + res.clearCookie('token', { + domain:'.bssm.kro.kr', + path:'/', + }); + res.clearCookie('token', { + domain:'bssm.kro.kr', + path:'/', + }); + throw new UnAuthorizedException('Need to relogin'); } + // db에서 리프레시 토큰 사용이 가능한지 확인 - let rows - const getTokenQuery="SELECT * FROM `tokens` WHERE `token`=? AND `valid`=1"; - try{ - [rows] = await pool.query(getTokenQuery, [result.token]); - }catch(err){ - console.error(err) - return res.send(JSON.stringify({status:2,subStatus:0})); - } - if(!rows[0]){ - // 리프레시 토큰이 db에서 사용불가 되었으면 로그인을 요청 + const tokenInfo = await tokenRepository.getToken(result.token); + // 리프레시 토큰이 db에서 사용불가 되었으면 로그인을 요청 + if(tokenInfo === null){ res.clearCookie('refreshToken', { domain:'.bssm.kro.kr', path:'/', }); - return res.send(JSON.stringify({status:4,subStatus:5})); + res.clearCookie('refreshToken', { + domain:'bssm.kro.kr', + path:'/', + }); + res.clearCookie('token', { + domain:'.bssm.kro.kr', + path:'/', + }); + res.clearCookie('token', { + domain:'bssm.kro.kr', + path:'/', + }); + throw new UnAuthorizedException('Need to relogin'); } - rows = rows[0] + // 유저 정보를 가져옴 - const dbResult = await accountRepository.getMemberByCode(rows.member_code); + const memberInfo = await accountRepository.getMemberByCode(tokenInfo.member_code); + if(memberInfo === null){ + res.clearCookie('refreshToken', { + domain:'.bssm.kro.kr', + path:'/', + }); + res.clearCookie('refreshToken', { + domain:'bssm.kro.kr', + path:'/', + }); + res.clearCookie('token', { + domain:'.bssm.kro.kr', + path:'/', + }); + res.clearCookie('token', { + domain:'bssm.kro.kr', + path:'/', + }); + throw new UnAuthorizedException('Need to relogin'); + } + const payload = { isLogin:true, - memberCode:dbResult.member_code, - memberId:dbResult.member_id, - memberNickname:dbResult.member_nickname, - memberLevel:dbResult.member_level, - grade:dbResult.member_grade, - classNo:dbResult.member_class, - studentNo:dbResult.member_studentNo + memberCode:memberInfo.member_code, + memberId:memberInfo.member_id, + memberNickname:memberInfo.member_nickname, + memberLevel:memberInfo.member_level, + grade:memberInfo.member_grade, + classNo:memberInfo.member_class, + studentNo:memberInfo.member_studentNo } // 액세스 토큰 재발행 const token = jwt.sign(payload, secretKey, { @@ -159,8 +203,13 @@ const refreshToken = async (req:express.Request, res:express.Response, next:expr secure:true, maxAge:1000*60*60// 1시간 동안 저장 1000ms*60초*60분 }); - return res.send(JSON.stringify({status:4,subStatus:4,token:token})); + return res.status(401).send(JSON.stringify({ + statusCode:401, + message:'token updated', + token + })); } + export { sign, login, diff --git a/src/view/board.controller.js b/src/view/board.controller.js index 379c8cf..ce3613c 100644 --- a/src/view/board.controller.js +++ b/src/view/board.controller.js @@ -1,70 +1,26 @@ -const express = require('express') -const router = express.Router() -const jwt = require('../util/jwt') +const express = require('express'); +const router = express.Router(); router.get('/write/:boardType', (req ,res) => { - const jwtValue = jwt.check(req.cookies.token); res.render('post_write', { - member:{ - isLogin:jwtValue.isLogin, - code:jwtValue.memberCode, - id:jwtValue.memberId, - nickname:jwtValue.memberNickname, - level:jwtValue.memberLevel, - grade:jwtValue.grade, - classNo:jwtValue.classNo, - studentNo:jwtValue.studentNo, - }, boardType:req.params.boardType, postNo:null, - }) -}) + }); +}); + router.get('/write/:boardType/:postNo', (req ,res) => { - const jwtValue = jwt.check(req.cookies.token); res.render('post_write', { - member:{ - isLogin:jwtValue.isLogin, - code:jwtValue.memberCode, - id:jwtValue.memberId, - nickname:jwtValue.memberNickname, - level:jwtValue.memberLevel, - grade:jwtValue.grade, - classNo:jwtValue.classNo, - studentNo:jwtValue.studentNo, - }, boardType:req.params.boardType, postNo:req.params.postNo, - }) -}) + }); +}); + router.get('/:boardType', (req ,res) => { - const jwtValue = jwt.check(req.cookies.token); - res.render('board', { - member:{ - isLogin:jwtValue.isLogin, - code:jwtValue.memberCode, - id:jwtValue.memberId, - nickname:jwtValue.memberNickname, - level:jwtValue.memberLevel, - grade:jwtValue.grade, - classNo:jwtValue.classNo, - studentNo:jwtValue.studentNo, - } - }) -}) + res.render('board'); +}); + router.get('/:boardType/:postNo', (req ,res) => { - const jwtValue = jwt.check(req.cookies.token); - res.render('board', { - member:{ - isLogin:jwtValue.isLogin, - code:jwtValue.memberCode, - id:jwtValue.memberId, - nickname:jwtValue.memberNickname, - level:jwtValue.memberLevel, - grade:jwtValue.grade, - classNo:jwtValue.classNo, - studentNo:jwtValue.studentNo, - } - }) -}) + res.render('board'); +}); module.exports = router; \ No newline at end of file diff --git a/src/view/view.controller.js b/src/view/view.controller.js index 1c0fb70..37682be 100644 --- a/src/view/view.controller.js +++ b/src/view/view.controller.js @@ -1,92 +1,37 @@ -const express = require('express') -const router = express.Router() -const jwt = require('../util/jwt') - +const express = require('express'); +const router = express.Router(); +const jwt = require('../util/jwt'); const boardController = require('./board.controller'); router.get('/', (req ,res) => { - const jwtValue = jwt.check(req.cookies.token); - res.render('index', { - member:{ - isLogin:jwtValue.isLogin, - code:jwtValue.memberCode, - id:jwtValue.memberId, - nickname:jwtValue.memberNickname, - level:jwtValue.memberLevel, - grade:jwtValue.grade, - classNo:jwtValue.classNo, - studentNo:jwtValue.studentNo, - } - }) + res.render('index'); }) + router.get('/memberinfo/:memberCode', (req ,res) => { - const jwtValue = jwt.check(req.cookies.token); res.render('memberinfo', { - member:{ - isLogin:jwtValue.isLogin, - code:jwtValue.memberCode, - id:jwtValue.memberId, - nickname:jwtValue.memberNickname, - level:jwtValue.memberLevel, - grade:jwtValue.grade, - classNo:jwtValue.classNo, - studentNo:jwtValue.studentNo, - }, memberCode:req.params.memberCode - }) + }); }) + router.get('/meal', (req ,res) => { - const jwtValue = jwt.check(req.cookies.token); - res.render('meal', { - member:{ - isLogin:jwtValue.isLogin, - code:jwtValue.memberCode, - id:jwtValue.memberId, - nickname:jwtValue.memberNickname, - level:jwtValue.memberLevel, - grade:jwtValue.grade, - classNo:jwtValue.classNo, - studentNo:jwtValue.studentNo, - } - }) + res.render('meal'); }) + router.get('/timetable', (req ,res) => { - const jwtValue = jwt.check(req.cookies.token); - res.render('timetable', { - member:{ - isLogin:jwtValue.isLogin, - code:jwtValue.memberCode, - id:jwtValue.memberId, - nickname:jwtValue.memberNickname, - level:jwtValue.memberLevel, - grade:jwtValue.grade, - classNo:jwtValue.classNo, - studentNo:jwtValue.studentNo, - } - }) + res.render('timetable'); }) + router.get('/meister', (req ,res) => { - const jwtValue = jwt.check(req.cookies.token); - res.render('meister', { - member:{ - isLogin:jwtValue.isLogin, - code:jwtValue.memberCode, - id:jwtValue.memberId, - nickname:jwtValue.memberNickname, - level:jwtValue.memberLevel, - grade:jwtValue.grade, - classNo:jwtValue.classNo, - studentNo:jwtValue.studentNo, - } - }) + res.render('meister'); }) + router.get('/pwReset', (req ,res) => { const jwtValue = jwt.verify(req.query.token); if(jwtValue=='EXPIRED'){ - return res.send("토큰 유효기간이 만료되었습니다") + return res.send("토큰 유효기간이 만료되었습니다"); } if(!jwtValue.pwEdit){ - return res.send("정상적인 접근이 아닙니다") + return res.send("정상적인 접근이 아닙니다"); } res.cookie('token', req.query.token, { path:"/", @@ -94,48 +39,15 @@ router.get('/pwReset', (req ,res) => { secure:true, maxAge:1000*60*5 }); - res.render('etc/pwReset', { - member:{ - isLogin:false, - code:null, - id:null, - nickname:null, - level:null, - grade:null, - classNo:null, - studentNo:null, - } - }) + res.render('etc/pwReset'); }) + router.get('/emoticon', (req ,res) => { - const jwtValue = jwt.check(req.cookies.token); - res.render('emoticon', { - member:{ - isLogin:jwtValue.isLogin, - code:jwtValue.memberCode, - id:jwtValue.memberId, - nickname:jwtValue.memberNickname, - level:jwtValue.memberLevel, - grade:jwtValue.grade, - classNo:jwtValue.classNo, - studentNo:jwtValue.studentNo, - } - }) + res.render('emoticon'); }) + router.get('/login', (req ,res) => { - const jwtValue = jwt.check(req.cookies.token); - res.render('etc/login', { - member:{ - isLogin:jwtValue.isLogin, - code:jwtValue.memberCode, - id:jwtValue.memberId, - nickname:jwtValue.memberNickname, - level:jwtValue.memberLevel, - grade:jwtValue.grade, - classNo:jwtValue.classNo, - studentNo:jwtValue.studentNo, - } - }) + res.render('etc/login'); }) router.use('/board', boardController);