어제 만든 토큰을
인증키 검증을 통해 맞다면 json 64비트로 만든걸 글자형으로 다시 만들기
$npm i nunjucks
+ 코드 작성
const nunjucks=require('nunjucks'); nunjucks.configure('views',{ express:app, }) app.set('view engine', 'html'); app.get('/', (req,res)=>{ res.render('index.html') })
로그인 버튼 누르면 로그인 팝업 나오게 만들기 (window.open 말고 layer popup 으로!)
css,JS 를 사용해보기 static 정적파일 사용할 수 있게 세팅하기
app.use(express.static('public'));
파일 구조
views - index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link href="./css/main.css" rel = "stylesheet"> <script type="text/javascript" src="./js/main.js"></script> </head> <body> <button id = "loginBtn">로그인</button> <div class ="layerPopup"> <div class="loginPopup"> <div id = "loginForm"> //form -> div 변경 <h1>로그인 페이지</h1> <input type="text" name = "userid" id="userid" placeholder="아이디를 입력해주세요."> <input type="password" name="userpw" id="userpw" placeholder="패스워드를 입력해주세요."> <button id = "localLogin" class="loginSubmit" >로그인</button> <button id = "kakaoLogin" class = "loginSubmit kakao">카카오로그인</button> <p class="joinWord">아직도 회원이 아니세요? <span class="joinBtn">회원가입</span> </p> </div> </div> </div> </body> </html>
html에 css, js 잘 연결되었는지 확인해보기 ↓↓
public - css - main.css
*{ margin:0; padding:0; color:darkblue; }
publick - js - main.js
alert('dd');
main.css
*{ margin:0; padding:0; } .layerPopup{ position:fixed; top:0; width:100%; height:100%; background:#000000a6; } .loginPopup{ width:400px; height:300px; background:white; box-sizing:border-box; padding:0 50px 50px 50px; position:absolute; left:50%; top:50%; transform: translate(-50%,-50%); } #loginForm>h1{ margin-top:32px; } #loginForm>input{ width:100%; box-sizing:border-box; padding:7px 14px; margin-top:10px; border:1px solid #666; } #loginForm>.loginSubmit{ width:100%; height:30px; border:1px solid #666; background:#fff; margin-top:10px; cursor:pointer; border-radius:15px; } #loginForm>.loginSubmit.kakao{ background:yellow; } .joinWord{ margin-top:10px; text-align: left; font-size: 12px; } .joinWord>.joinBtn{ float:right; color:blue; cursor:pointer; }
.layerPopup display: none 주기 & open도
.layerPopup{ display:none; position:fixed; top:0; width:100%; height:100%; background:#000000a6; } .layerPopup.open{ display:block; }
main.js
document.addEventListener('DOMContentLoaded',init); // 딱 한번 최초 실행 function init(){ const loginBtn = document.querySelector('#loginBtn'); loginBtn.addEventListener('click',loginBtnFn) } function loginBtnFn(){ const layerPopup =document.querySelector('.layerPopup'); // layerPopup.setAttribute('class', 'layerPopup open'); layerPopup.classList.add('open'); //오우 }
여기까지 localhost:3000 -> 로그인 버튼 클릭 -> 로그인 페이지 나오게 만듬!
검정 배경 클릭했을 때 닫히게 만들기
js
document.addEventListener('DOMContentLoaded',init); // 딱 한번 최초 실행 function init(){ const loginBtn = document.querySelector('#loginBtn'); const layerPopup=document.querySelector('.layerPopup'); loginBtn.addEventListener('click',loginBtnFn) layerPopup.addEventListener('click', layerPopupClose); } function loginBtnFn(){ const layerPopup =document.querySelector('.layerPopup'); // layerPopup.setAttribute('class', 'layerPopup open'); layerPopup.classList.add('open'); //오우 } function layerPopupClose(event){ console.log(event) }
console.log(event, this) 찍어보기
검정배경을 클릭하면 사라지지만 하얀 배경 클릭하면 안사라지게 만들기 !
js
document.addEventListener('DOMContentLoaded',init); // 딱 한번 최초 실행 function init(){ const loginBtn = document.querySelector('#loginBtn'); const layerPopup=document.querySelector('.layerPopup'); loginBtn.addEventListener('click',loginBtnFn) layerPopup.addEventListener('click', layerPopupClose); } function loginBtnFn(){ const layerPopup =document.querySelector('.layerPopup'); // layerPopup.setAttribute('class', 'layerPopup open'); layerPopup.classList.add('open'); //오우 } function layerPopupClose(event){ console.log(event, this) if(event.target==this){ this.classList.remove('open'); } }
로그인버튼 눌렀을 때 서버에 요청하는 거 만들기
fetch로 내용 전달
main.js
document.addEventListener('DOMContentLoaded',init); // 딱 한번 최초 실행 function init(){ const loginBtn = document.querySelector('#loginBtn'); const layerPopup=document.querySelector('.layerPopup'); const localLogin=document.querySelector('#localLogin'); loginBtn.addEventListener('click',loginBtnFn) layerPopup.addEventListener('click', layerPopupClose); localLogin.addEventListener('click',localLoginFn); } function loginBtnFn(){ const layerPopup =document.querySelector('.layerPopup'); // layerPopup.setAttribute('class', 'layerPopup open'); layerPopup.classList.add('open'); //오우 } function layerPopupClose(event){ if(event.target==this){ this.classList.remove('open'); } } async function localLoginFn(){ const userid = document.querySelector('userid'); const userpw = document.querySelector('userpw'); if(userid.value==""){ alert('아이디를 입력해 주세요'); userid.focus(); return 0; // return을 끝낸다는 의미 //return 안쓰면 if 절 밖에 fetch가 실행될거야 } if(userpw.value==""){ alert('패스워드를 입력해주세요.'); userpw.focus(); return 0; } //POST auth/local/login let url = `http://localhost:3000/auth/local/login`; let options = { method:'POST', //userid,userpw값 두개를 보낼거야 server측에서 받을 수 있는 방법 2가지 //1. 일방적인 방법 key=value&key2=value2 headers:{ 'content-type':'application/x-www-form-encoded' }, //post로 보냈고 queryString으로 보냈구나 알도록 headers추가 body:`userid=${userid.value}&userpw=${userpw.value}`, } let response = await fetch(url,options); console.log(response) }
$npm i body-parser
코드 추가
const bodyParser = require('body-parser'); app.user(bodyParser.urlencoded({extended:false,}));
여기까지 server.js
const express=require('express'); const cookieParser=require('cookie-parser'); const tokenKey=require('./key'); const nunjucks=require('nunjucks'); const bodyParser = require('body-parser'); const app = express(); nunjucks.configure('views',{ express:app, }) app.set('view engine', 'html'); app.use(express.static('public')); app.use(bodyParser.urlencoded({extended:false,})); app.use(cookieParser()); app.get('/', (req,res)=>{ res.render('index.html') }) app.post('/auth/local/login',(req,res)=>{ let {userid,userpw} = req.body; console.log(userid,userpw); res.json({ result:true, msg:'로그인에 성공했씁니다' }) }) app.get('/login',(req,res)=>{ let {id,pw} = req.query; if(id=='root' && pw=='root'){ res.cookie('token',tokenKey, {httpOnly:true,secure:true}); res.redirect('/?msg=로그인성공'); }else{ res.redirect('/?msg=id와 pw가 일치하지 않습니다.'); } }) app.get('/menu1',(req,res)=>{ console.log(req.cookies); res.send('menu1페이지입니다'); }) app.listen(3000,()=>{ console.log('start port : 3000'); })
public - main.js
document.addEventListener('DOMContentLoaded',init); // 딱 한번 최초 실행 function init(){ const loginBtn = document.querySelector('#loginBtn'); const layerPopup=document.querySelector('.layerPopup'); const localLogin=document.querySelector('#localLogin'); loginBtn.addEventListener('click',loginBtnFn) layerPopup.addEventListener('click', layerPopupClose); localLogin.addEventListener('click',localLoginFn); } function loginBtnFn(){ const layerPopup =document.querySelector('.layerPopup'); // layerPopup.setAttribute('class', 'layerPopup open'); layerPopup.classList.add('open'); //오우 } function layerPopupClose(event){ if(event.target==this){ this.classList.remove('open'); } } async function localLoginFn(){ const userid = document.querySelector('#userid'); const userpw = document.querySelector('#userpw'); if(userid.value==""){ alert('아이디를 입력해 주세요'); userid.focus(); return 0; // return을 끝낸다는 의미 //return 안쓰면 if 절 밖에 fetch가 실행될거야 } if(userpw.value==""){ alert('패스워드를 입력해주세요.'); userpw.focus(); return 0; } //POST auth/local/login let url = `http://localhost:3000/auth/local/login`; let options = { method:'POST', //userid,userpw값 두개를 보낼거야 server측에서 받을 수 있는 방법 2가지 //1. 일방적인 방법 key=value&key2=value2 headers:{ 'content-type':'application/x-www-form-urlencoded' }, //post로 보냈고 queryString으로 보냈구나 알도록 headers추가 body:`userid=${userid.value}&userpw=${userpw.value}`, } let response = await fetch(url,options); console.log(response) }
public - index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link href="./css/main.css" rel = "stylesheet"> <script type="text/javascript" src="./js/main.js"></script> </head> <body> <button id = "loginBtn">로그인</button> <div class ="layerPopup"> <div class="loginPopup"> <div id = "loginForm"> <h1>로그인 페이지</h1> <input type="text" name = "userid" id="userid" placeholder="아이디를 입력해주세요."> <input type="password" name="userpw" id="userpw" placeholder="패스워드를 입력해주세요."> <button id = "localLogin" class="loginSubmit" >로그인</button> <button id = "kakaoLogin" class = "loginSubmit kakao">카카오로그인</button> <p class="joinwWord">아직도 회원이 아니세요? <span class="joinBtn">회원가입</span> </p> </div> </div> </div> </body> </html>
pupblic- main.css
*{ margin:0; padding:0; } .layerPopup{ display:none; position:fixed; top:0; width:100%; height:100%; background:#000000a6; } .layerPopup.open{ display:block; } .loginPopup{ width:400px; height:300px; background:white; box-sizing:border-box; padding:0 50px 50px 50px; position:absolute; left:50%; top:50%; transform: translate(-50%,-50%); } #loginForm>h1{ margin-top:32px; } #loginForm>input{ width:100%; box-sizing:border-box; padding:7px 14px; margin-top:10px; border:1px solid #666; } #loginForm>.loginSubmit{ width:100%; height:30px; border:1px solid #666; background:#fff; margin-top:10px; cursor:pointer; border-radius:15px; } #loginForm>.loginSubmit.kakao{ background:yellow; } .joinWord{ margin-top:10px; text-align: left; font-size: 12px; } .joinWord>.joinBtn{ float:right; color:blue; cursor:pointer; }
-------------------------------------body 보내는 방법 1 끗 (body - form type)--------------------------------------
-------------------------------이제 body 보내는 방법 2번째 시작 (body - json형식으로)--------------------------------
main.js
let url = `http://localhost:3000/auth/local/login`; let options={ method:'POST', headers:{ 'content-type':`application/json` }, body:JSON.stringify({ userid:userid.value, userpw:userpw.value, }), } let response = await fetch(url,options); console.log(response)
요청은 잘 들어갔음 (요청헤더, body쪽 봐봐) 근데 console.log = undefined
-> middleware 하나 놔주기 in server.js
app.use(bodyParser.json());
body에 json 오면 받을 수 있게 해라 ~ server on -> console 잘 뜸
queryString으로 보내면 content Type - x-www 이거
json type 이면 content type - json + app.use(bodyParser.json()
API 요청할 때 웬만하면 JSON 형태로 보내자 header에다가 넣어서도 보낼 수 잇음.
main.js
let url = `http://localhost:3000/auth/local/login`;
let options = {
method: 'POST',
headers: {
'content-type': `application/json`,
'data': JSON.stringify({
userid: userid.value, userpw: userpw.value
})
},
body: JSON.stringify({
userid: userid.value,
userpw: userpw.value,
}),
}
req.get() -> headers의 속성값에 접근할 수 있음
string으로 받은거 다시 json으로 바꿔서 사용해야함
server.js
app.post('/auth/local/login', (req, res) => {
//let {userid,userpw} = req.body;
let { userid, userpw } = JSON.parse(req.get('data'));
//String -> 객체 형태로 돌리기
console.log('data req: ', userid, userpw);
//console.log('body req: ',userid,userpw);
res.json({ result: true, msg: '로그인에 성공했씁니다' })
})
요청자에 따라 header, body 에 넣어주세요 ~ 이렇게 요구가 달라서 다 알아야함
다시 돌려놓기
server.js
app.post('/auth/local/login',(req,res)=>{ let {userid,userpw} = req.body; //let {userid, userpw} = JSON.parse(req.get('data')); //String -> 객체 형태로 돌리기 //console.log('data req: ', userid, userpw); console.log('body req: ',userid,userpw); res.json({ result:true, msg:'로그인에 성공했씁니다' }) })
main.js
let url = `http://localhost:3000/auth/local/login`; let options={ method:'POST', headers:{ 'content-type':`application/json`, }, body:JSON.stringify({ userid:userid.value, userpw:userpw.value, }), }
로그인 성공여부 / db는 ㄴㄴ
server.js
app.post('/auth/local/login',(req,res)=>{ let {userid,userpw} = req.body; console.log('body req: ',userid,userpw); //원래는 DB접속 후 결과 return let result={}; if(userid=="asdf"&&userpw=="asdf"){ result = { result:true, msg:'로그인에 성공햇습니다.' } }else{ result={ result:false, msg:'아이디와 패스워드를 확인해주세요' } } res.json(result); })
defualt로 실패를 깔고 가도 가능
main.js
async function localLoginFn() {
const userid = document.querySelector('#userid');
const userpw = document.querySelector('#userpw');
if (userid.value == "") {
alert('아이디를 입력해 주세요');
userid.focus();
return 0; // return을 끝낸다는 의미 //return 안쓰면 if 절 밖에 fetch가 실행될거야 }
}
if (userpw.value == "") {
alert('패스워드를 입력해주세요.');
userpw.focus(); return 0;
}
//POST auth/local/login
let url = `http://localhost:3000/auth/local/login`;
let options = {
method: 'POST',
headers: { 'content-type': `application/json`, },
body: JSON.stringify({
userid: userid.value,
userpw: userpw.value,
})
}
let response = await fetch(url, options);
let res_result = await response.json(); //body내용 가져오기 promise객체 떨어지게
console.log(res_result)
}
로그인 성공했을 때 token 생성해주기
토큰 생성 코드 기니까 파일 따로 만들자 -jwt.js
$npm i crypto
$npm i dotenv
jwt.js (userid 를 담은 토큰 생성과정)
require('dotenv').config();
const crypto = require('crypto');
const { create } = require('domain');
//JWT 토큰 header.payload.signature
function createToken(userid) {
let header = { "tpy": "JWT", "alg": "HS256", }
//현재시간으로부터 미래시간 : 2시간을 더한 수
//두시간 뒤 재로그인 - 길면 길수록 해킹 위험 up
let exp = new Date().getTime() + ((60 * 60 * 2) * 1000) //1970.1.1부터 0 // 1월1일 1초지났으면 1000
let payload = {
//인자로 들어온 애
userid,
//expiary date
exp,
}
///Buffer.from - 바이너리 파일 만들기
const encodingHeader = Buffer.from(JSON.stringify(header))
.toString('base64')
.replace('=', '').replace('=', '');
const encodingPayload = Buffer.from(JSON.stringify(payload))
.toString('base64')
.replace('=', '').replace('=', '');
//console.log(encodingHeader, encodingPayload);
//암호화 시작 //첫번째 : 암호방식 /두번째인자 : key값 (이게 공개되면 해킹됨) - best-env
let signature = crypto.createHmac('sha256', Buffer.from(process.env.salt))
.update(encodingHeader + "." + encodingPayload)
.digest('base64')
.replace('=', '').replace('=', '');
let jwt = `${encodingHeader}.${encodingPayload}.${signature}` return jwt;
}
let token = createToken('too');
console.log(token)
//userid 값만 넣어줄 예정
//module.exports=(userid)=>{ } 아래와 같은 뜻
module.exports = createToken;
console.log에 찍힌 값 -> JWT사이트가서 확인 - > secret base 62 endoce 체크 -> Signature Verified
token 으로 가져오기 in
const tokenKey=require('./jwt')
server.js
const express = require('express');
const cookieParser = require('cookie-parser');
const nunjucks = require('nunjucks');
const bodyParser = require('body-parser');
const tokenKey = require('./jwt')
const app = express();
nunjucks.configure('views', { express: app, })
app.set('view engine', 'html');
app.use(express.static('public'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false, }));
app.use(cookieParser());
app.get('/', (req, res) => {
res.render('index.html')
})
app.post('/auth/local/login', (req, res) => {
let { userid, userpw } = req.body;
console.log('body req: ', userid, userpw);
//원래는 DB접속 후 결과 return
let result = {};
if (userid == "asdf" && userpw == "asdf") {
result = {
result: true, msg: '로그인에 성공햇습니다.'
} //여기에 token 을 생성해주면 됨
let token = tokenKey
res.cookie('ccessToken', token, { httpOnly: true, secure: true, });
} else {
result = {
result: false, msg: '아이디와 패스워드를 확인해주세요'
}
}
res.json(result);
})
app.get('/login', (req, res) => {
let { id, pw } = req.query;
if (id == 'root' && pw == 'root') {
res.cookie('token', tokenKey, { httpOnly: true, secure: true });
res.redirect('/?msg=로그인성공');
} else {
res.redirect('/?msg=id와 pw가 일치하지 않습니다.');
}
})
app.get('/menu1', (req, res) => {
console.log(req.cookies);
res.send('menu1페이지입니다');
})
app.listen(3000, () => { console.log('start port : 3000'); })
main.js
document.addEventListener('DOMContentLoaded', init); // 딱 한번 최초 실행
function init() {
const loginBtn = document.querySelector('#loginBtn');
const layerPopup = document.querySelector('.layerPopup');
const localLogin = document.querySelector('#localLogin');
loginBtn.addEventListener('click', loginBtnFn)
layerPopup.addEventListener('click', layerPopupClose);
localLogin.addEventListener('click', localLoginFn);
}
function loginBtnFn() {
const layerPopup = document.querySelector('.layerPopup');
// layerPopup.setAttribute('class', 'layerPopup open');
layerPopup.classList.add('open'); //오우
}
function layerPopupClose(event) {
if (event.target == this) {
this.classList.remove('open');
}
}
async function localLoginFn() {
const userid = document.querySelector('#userid');
const userpw = document.querySelector('#userpw');
if (userid.value == "") {
alert('아이디를 입력해 주세요');
userid.focus();
return 0; // return을 끝낸다는 의미 //return 안쓰면 if 절 밖에 fetch가 실행될거야
}
if (userpw.value == "") {
alert('패스워드를 입력해주세요.');
userpw.focus();
return 0;
} //POST auth/local/login
let url = `http://localhost:3000/auth/local/login`;
let options = {
method: 'POST',
headers: { 'content-type': `application/json`, },
body: JSON.stringify({
userid: userid.value, userpw: userpw.value,
})
}
let response = await fetch(url, options);
let res_result = await response.json(); //body내용 가져오기 promise객체 떨어지게
console.log(res_result) //res_result 안에 result, msg 라는 속성이 있음
let { result, msg } = res_result; alert(msg);
if (result) {
//로그인 성공
let layerPopup = document.querySelector('.layerPopup');
layerPopup.classList.remove('open');
} else {
userid.value == '';
userpw.value == '';
userid.focus();
}
}
'sha256' 암호 방식 -> salt = asdfasdf
저 값은 우리만의 열☆쇠
middleware 폴더 - auth.js 파일 만들기
require('dotenv').config();
const crypto = require('crypto');
const tokenKey = require('../jwt');
let token = tokenKey('root');
console.log(token);
console.log 찍을 떄 주의사항 폴더 안에 있는거라 폴더값까지 써야해
토큰에서 . 쩜을 구분으로 배열로 나눠서 담기
require('dotenv').config();
const crypto = require('crypto');
const tokenKey = require('../jwt');
let accessToken = tokenKey('root');
console.log(accessToken);
let tokenarray = accessToken.split('.');
let header=tokenarray[0];
let payload=tokenarray[1];
console.log(header, payload)
이렇게도 가능
require('dotenv').config();
const crypto = require('crypto');
const tokenKey = require('../jwt');
let accessToken = tokenKey('root');
console.log(accessToken);
let [header,payload] = accessToken.split('.');
console.log(header)
console.log(payload)
두개의 값으로 암호화를 진행하고 기존의 signature 와 같은지 쳌
SHA256 블록체인이랑도 관련있는 암호화 방식 - 단방향 암호화
기존에 만들었던걸 검증할 때 다시 만들어서 체크를 할 수밖에 없음 - 다르면 문제잇음
auth.js
require('dotenv').config();
const crypto = require('crypto');
const tokenKey = require('../jwt');
let accessToken = tokenKey('root');
//client의 cookie.accesstoken값
console.log(accessToken);
//let tokenarray = accessToken.split('.');
//let header=tokenarray[0];
//let payload=tokenarray[1];
let [header, payload, sign] = accessToken.split('.');
let signature = getSignature(header, payload);
console.log(signature)
if (sign == signature) {
console.log('검증된 토큰입니다.');
} else {
console.log('해킹이다.')
}
function getSignature(header, payload) {
const signature = crypto.createHmac('sha256', Buffer.from(process.env.salt))
.update(header + "." + payload)
.digest('base64')
.replace('=', '').replace('==', '');
return signature;
}
console.log 해보면 '검증된 토큰입니다'
이제 다시 원래 형태로 복원하기
복원 함수가 js 는 있음
Buffer.from ()
첫번째 인자값 : payload
두번째 인자값 : 'base64'
base64인데 또 바이너리로 만든다고? -> 두번쨰 인자값 넣어 -> 바이너리 데이터로 표현됨 => String으로 바꿔주기
String -> Object로 JSON.parse 를 통해 만들기
얘를 각각 변수에 담기
if(sign==signature){
console.log('검증된 토큰입니다.');
let {userid,exp}=JSON.parse(Buffer.from(payload, 'base64')
.toString());
console.log(userid,exp)
}else{
console.log('해킹이다.') }
exp : token 생성한 시간으로부터 2시간 뒤를 저장한 getTim()
요 2시간이 만료되었는지 if 조건문 넣기
require('dotenv').config();
const crypto = require('crypto');
const tokenKey = require('../jwt');
let accessToken = tokenKey('root');
//client의 cookie.accesstoken값
console.log(accessToken);
let [header, payload, sign] = accessToken.split('.');
let signature = getSignature(header, payload);
console.log(signature)
if (sign == signature) {
console.log('검증된 토큰입니다.');
let { userid, exp } = JSON.parse(Buffer.from(payload, 'base64').toString());
console.log(userid, exp)
//시간 넘었는지 쳌
let newExp = new Date().getTime();
if (newExp > exp) {
console.log('토큰 만료되엇스빈다.');
return 0;
} //여기까지 다 검증되면 이제 userid를 사용해도 된다 .
console.log(userid);
} else {
console.log('해킹이다.')
}
function getSignature(header, payload) {
const signature = crypto.createHmac('sha256', Buffer.from(process.env.salt))
.update(header + "." + payload).digest('base64')
.replace('=', '').replace('==', ''); return signature;
}
이제 userid 사용하려면 middleware 통해서 사용
미들웨어 완료
require('dotenv').config();
const crypto = require('crypto');
const tokenKey = require('../jwt');
module.exports = (req, res, next) => {
let { accessToken } = req.cookies; //client의 cookie.accesstoken값
if (accessToken == undefined) {
res.json({ result: false, msg: '로그인을 진행해줍쇼' });
}
let [header, payload, sign] = accessToken.split('.');
let signature = getSignature(header, payload);
console.log(signature)
if (sign == signature) {
console.log('검증된 토큰입니다.');
let { userid, exp } = JSON.parse(Buffer.from(payload, 'base64').toString());
console.log(userid, exp) //시간 넘었는지 쳌
let newExp = new Date().getTime();
if (newExp > exp) {
res.redirect('/?msg=토큰만료요')
} //모든 검증이 완료됨 - 이제 userid를 사용해도 된다 .
req.userid = userid;
next();
} else {
res.redirect('/?msg=해킹이요');
}
}
function getSignature(header, payload) {
const signature = crypto.createHmac('sha256', Buffer.from(process.env.salt))
.update(header + "." + payload)
.digest('base64')
.replace('=', '').replace('==', '');
return signature;
}
server.js - 미들웨어 auth 가져오기
const auth = require('./middleware/auth')
server.js /user/info 생성 - 미들웨어 info에 넣기
app.get('/user/info',auth, (req,res)=>{
res.send('user info입니다< br/> hello ${req.userid}')
})
index.html
<body> <button id = "loginBtn">로그인</button> <a href="/user/info">회원정보 보기</a> //추가 <div class ="layerPopup"> <div class="loginPopup">
cookie값 지우기
auth.js
if (newExp > exp) { res.clearCookie('accessToken'); res.redirect('/?msg=토큰만료요') }
res.json ({}) 부분 -> redirect로 다 바꿔줌 auth.js - 3부분
전체 코드
server.js
const express = require('express');
const cookieParser = require('cookie-parser');
const nunjucks = require('nunjucks');
const bodyParser = require('body-parser');
const tokenKey = require('./jwt')
const auth = require('./middleware/auth')
const app = express();
nunjucks.configure('views', { express: app, })
app.set('view engine', 'html');
app.use(express.static('public'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false, }));
app.use(cookieParser());
app.get('/user/info', auth, (req, res) => {
res.send(`user info입니다 hello ${req.userid}`)
})
app.get('/', (req, res) => {
res.render('index.html')
})
app.post('/auth/local/login', (req, res) => {
let { userid, userpw } = req.body;
console.log('body req: ', userid, userpw); //원래는 DB접속 후 결과 return
let result = {};
if (userid == "asdf" && userpw == "asdf") {
result = { result: true, msg: '로그인에 성공햇습니다.' }
//여기에 token 을 생성해주면 됨
let token = tokenKey(userid)
res.cookie('accessToken', token, { httpOnly: true, secure: true, });
} else {
result = { result: false, msg: '아이디와 패스워드를 확인해주세요' }
}
res.json(result);
})
app.listen(3000, () => { console.log('start port : 3000'); })
jwt.js
require('dotenv').config(); const crypto = require('crypto'); const { create } = require('domain'); //JWT 토큰 header.payload.signature function createToken(userid){ let header = { "tpy":"JWT", "alg":"HS256", } //현재시간으로부터 미래시간 : 2시간을 더한 수 //두시간 뒤 재로그인 - 길면 길수록 해킹 위험 up let exp = new Date().getTime() + ((60*60*2) *1000) //1970.1.1부터 0 // 1월1일 1초지났으면 1000 let payload = { //인자로 들어온 애 userid, //expiary date exp, } ///Buffer.from - 바이너리 파일 만들기 const encodingHeader = Buffer.from(JSON.stringify(header)).toString('base64').replace('=','').replace('==',''); const encodingPayload = Buffer.from(JSON.stringify(payload)).toString('base64').replace('==','').replace('=',''); //console.log(encodingHeader, encodingPayload); //암호화 시작 //첫번째 : 암호방식 /두번째인자 : key값 (이게 공개되면 해킹됨) - best-env let signature = crypto.createHmac('sha256',Buffer.from(process.env.salt)).update(encodingHeader+"."+encodingPayload).digest('base64').replace('=','').replace('==',''); let jwt = `${encodingHeader}.${encodingPayload}.${signature}` return jwt; } let token = createToken('root'); console.log(token) //userid 값만 넣어줄 예정 //module.exports=(userid)=>{ } 아래와 같은 뜻 module.exports = createToken;
views- index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link href="./css/main.css" rel = "stylesheet"> <script type="text/javascript" src="./js/main.js"></script> </head> <body> <button id = "loginBtn">로그인</button> <a href="/user/info">회원정보 보기</a> <div class ="layerPopup"> <div class="loginPopup"> <div id = "loginForm"> <h1>로그인 페이지</h1> <input type="text" name = "userid" id="userid" placeholder="아이디를 입력해주세요."> <input type="password" name="userpw" id="userpw" placeholder="패스워드를 입력해주세요."> <button id = "localLogin" class="loginSubmit" >로그인</button> <button id = "kakaoLogin" class = "loginSubmit kakao">카카오로그인</button> <p class="joinwWord">아직도 회원이 아니세요? <span class="joinBtn">회원가입</span> </p> </div> </div> </div> </body> </html>
.env
salt=vneineign
public-js - main.js
document.addEventListener('DOMContentLoaded',init); // 딱 한번 최초 실행 function init(){ const loginBtn = document.querySelector('#loginBtn'); const layerPopup=document.querySelector('.layerPopup'); const localLogin=document.querySelector('#localLogin'); loginBtn.addEventListener('click',loginBtnFn) layerPopup.addEventListener('click', layerPopupClose); localLogin.addEventListener('click',localLoginFn); } function loginBtnFn(){ const layerPopup =document.querySelector('.layerPopup'); // layerPopup.setAttribute('class', 'layerPopup open'); layerPopup.classList.add('open'); //오우 } function layerPopupClose(event){ if(event.target==this){ this.classList.remove('open'); } } async function localLoginFn(){ const userid = document.querySelector('#userid'); const userpw = document.querySelector('#userpw'); if(userid.value==""){ alert('아이디를 입력해 주세요'); userid.focus(); return 0; // return을 끝낸다는 의미 //return 안쓰면 if 절 밖에 fetch가 실행될거야 } if(userpw.value==""){ alert('패스워드를 입력해주세요.'); userpw.focus(); return 0; } //POST auth/local/login let url = `http://localhost:3000/auth/local/login`; let options={ method:'POST', headers:{ 'content-type':`application/json`, }, body:JSON.stringify({ userid:userid.value, userpw:userpw.value, }) } let response = await fetch(url,options); let res_result = await response.json(); //body내용 가져오기 promise객체 떨어지게 console.log(res_result) //res_result 안에 result, msg 라는 속성이 있음 let {result,msg} = res_result; alert(msg); if(result){ //로그인 성공 let layerPopup = document.querySelector('.layerPopup'); layerPopup.classList.remove('open'); }else{ userid.value==''; userpw.value==''; userid.focus(); } }
public - main.css
*{ margin:0; padding:0; } .layerPopup{ display:none; position:fixed; top:0; width:100%; height:100%; background:#000000a6; } .layerPopup.open{ display:block; } .loginPopup{ width:400px; height:300px; background:white; box-sizing:border-box; padding:0 50px 50px 50px; position:absolute; left:50%; top:50%; transform: translate(-50%,-50%); } #loginForm>h1{ margin-top:32px; } #loginForm>input{ width:100%; box-sizing:border-box; padding:7px 14px; margin-top:10px; border:1px solid #666; } #loginForm>.loginSubmit{ width:100%; height:30px; border:1px solid #666; background:#fff; margin-top:10px; cursor:pointer; border-radius:15px; } #loginForm>.loginSubmit.kakao{ background:yellow; } .joinWord{ margin-top:10px; text-align: left; font-size: 12px; } .joinWord>.joinBtn{ float:right; color:blue; cursor:pointer; }
middleware-auth.js
require('dotenv').config(); const crypto = require('crypto'); const tokenKey = require('../jwt'); module.exports = (req, res, next) => { let {accessToken} = req.cookies; //client의 cookie.accesstoken값 if(accessToken==undefined){ res.redirect('/?msg=로그인을 해줍쇼'); return 0; } let [header, payload, sign] = accessToken.split('.'); let signature = getSignature(header, payload); console.log(signature) if (sign == signature) { console.log('검증된 토큰입니다.'); let { userid, exp } = JSON.parse(Buffer.from(payload, 'base64').toString()); console.log(userid, exp) //시간 넘었는지 쳌 let newExp = new Date().getTime(); if (newExp > exp) { res.clearCookie('accessToken'); res.redirect('/?msg=토큰만료요') } //모든 검증이 완료됨 - 이제 userid를 사용해도 된다 . // 이쪽에서 db 접속해서 처리 req.userid=userid; next(); } else { res.redirect('/?msg=해킹이요'); } } function getSignature(header, payload) { const signature = crypto.createHmac('sha256', Buffer.from(process.env.salt)).update(header + "." + payload).digest('base64').replace('=', '').replace('==', ''); return signature; }
'블록체인 기반 핀테크 및 응용 SW개발자 양성과정 일기' 카테고리의 다른 글
[JavaScript / Node.js] sha256 해시 알고리즘 암호화, 검증, 복호화 JWT 토큰 사용 정리 (1) | 2021.05.31 |
---|---|
[53일차 복습] JWT 예제 토큰 sha256 암호화, 복호화, 검증하기 with JavaScript, Node.js (0) | 2021.05.30 |
[52일차 복습] Javascript node.js [JWT] token 토큰 암호화해서 브라우저 cookie에 저장하기 (0) | 2021.05.27 |
[52일차]20210527 JWT token 토큰 만들어 브라우저 cookie에 저장하기 (0) | 2021.05.27 |
[51일차 복습] 카카오 API / 주소, 우편번호 요청 및 가져오기 (0) | 2021.05.26 |