/** bcrypt 모듈
단방향성 암호화로, 원래의 데이터를 아무도 모르게 만든다.
하지만 저장된 패스워드를 암호화 시키고, 들어온 패스워드를 검증이 한다.
yarn add bcrypt
**/
//데이터 값을 넣을 때 미리 db안에 만들어야 하므로 npx prisma db push를 먼저 하고 npx nodemon 을 하도록 하자.
/** refresh token 리프레쉬 토큰은 로그인 만료 기능을 수행 할 수 있게 만든다. 보안성이 많이 올라간다.
import express from 'express';
import jwt from 'jsonwebtoken';
import cookieParser from 'cookie-parser';
const app = express();
const PORT = 3019;
----비밀키 노출 방지를 위해 .env에 관리 할 것------
const ACCESS_TOKEN_SECRET_KEY = `HangHae99`;
const REFRESH_TOKEN_SECRET_KEY = `Sparta`;
app.use(express.json());
app.use(cookieParser());
const tokenStorages = {};
app.get('/', (req,res) => {
return res.status(200).send('Hello Token!');
});
-----------엑세스, 리프레시 토큰 발급-----------
app.post('/tokens, async (req,res) => {
const {id} = req.body; id값을 가져와서,
const accessToken = jwt.sign({id: id}, ACCESS_TOKEN_SECRET_KEY, {expiresIn: '10s'}); id, 비밀값, 파기날짜 순으로 넣는다.
const refreshToken = jwt.sign({id: id}, REFRESH_TOKEN_SECRET_KEY, {expiresIn: '7d'});
tokenStorages[refreshToken] = { 이곳에 tokenUserInfo 임시 저장
id: id,
ip: req.ip,
userAgent: req.headers['user-agent'],
}
res.cookie('accessToken', accessToken); 비밀화를 해서 토큰 발급
res.cookie('refreshToken', refreshToken);
return res.status(200).json({message: 'Token이 정상적으로 발급되었습니다.'})
});
엑세스 토큰 검증 API
app.get('/tokens/validate', (req, res) => {
const accessToken = req.cookies.accessToken; 클라이언트 쿠키값 가져와서
if (!accessToken) { 쿠키가 없으면?
return res
.status(400)
.json({ errorMessage: 'Access Token이 존재하지 않습니다.' });
}
const payload = validateToken(accessToken, ACCESS_TOKEN_SECRET_KEY); 맨 아래에 함수있다. 함수 끝나면 토큰 아니면 빈값
if (!payload) { 토큰이 이상하면 혹은 만료되면
return res
.status(401)
.json({ errorMessage: 'Access Token이 유효하지 않습니다.' });
}
const { id } = payload; 토큰값이 id로 들어감
return res.json({
message: `${id}의 Payload를 가진 Token이 성공적으로 인증되었습니다.`,
});
});
// Token을 검증하고 Payload를 반환합니다.
function validateToken(token, secretKey) {
try {
const payload = jwt.verify(token, secretKey);
return payload;
} catch (error) {
return null;
}
}
리프레시 토큰 검증 API
app.post('/tokens/refresh', (req, res) => {
const refreshToken = req.cookies.refreshToken; 리프레시 쿠키를 가져온다.
if (!refreshToken) 쿠키가 없으면,
return res
.status(400)
.json({ errorMessage: 'Refresh Token이 존재하지 않습니다.' });
const payload = validateToken(refreshToken, REFRESH_TOKEN_SECRET_KEY); 위에랑 동일
if (!payload) {
return res
.status(401)
.json({ errorMessage: 'Refresh Token이 유효하지 않습니다.' });
}
const userInfo = tokenStorage[refreshToken]; 서버에서 유저 정보를 가져온다.
if (!userInfo) 정보가 없으면,
return res.status(419).json({
errorMessage: 'Refresh Token의 정보가 서버에 존재하지 않습니다.',
});
const newAccessToken = createAccessToken(userInfo.id); (실수로 만료기간 안 적을 수도 있으니 그냥 함수로 뺄 것)
res.cookie('accessToken', newAccessToken); 리프레시 토큰이 있으면 액세스 토큰을 새로 받을 수 있다~
return res.json({ message: 'Access Token을 새롭게 발급하였습니다.' });
});
// Token을 검증하고 Payload를 반환합니다.
function validateToken(token, secretKey) { 토큰이랑 시크릿 키값이 들어오면
try {
const payload = jwt.verify(token, secretKey); verify를 이용해 토큰과 비밀값이 유효하면 토큰 리턴, 아니면 에러
return payload;
} catch (error) {
return null;
}
}
**/
/** 로그 미들웨어 : 코드가 실행되는 각종 정보에 대해 알려준다.
yarn add winston
import winston from 'winston';
const logger = winston.createLogger({ 로거를 만든다
level: 'info',
format: winston.format.json(),
transport:[
new winston.transports.Console(),
]
});
export default function(req,res,next){
const start = new Date().getTime(); 스타트는 지금 시간, 명령이 시작됐을때 시간
res.on('finish', () => { 끝났을 때
const duration = new Date().getTime() - start; 끝났을때 시간 - 명령 시작했을 때 시간
logger.info(`Method: ${req.method}, URL: ${req.url}, Status: ${res.statusCode}, Duration: ${duration}ms`);
});
next();
}
**/
/** 에러 처리 미들웨어
export default function (err, req, res, next) {
console.error(err);
res.status(500).json({message: '서버 내부에서 에러가 발생했습니다.'});
}
이후 route에 try catch 하고 넘겨주는값 next(err)
그리고 app.js 에 import 하고 에러를 전체적으로 처리해주기 위해 마지막에 작성한다.
**/