[Nodejs]생활코딩 Nodejs - 쿠키와 인증2
로그인 UI를 만들고, 로그인했을때 상태값을 나타내는 쿠키를 생성 상태값으로 로그인 여부를 확인하고, 그에 따라 접근권한을 부여함.
cookie.js
var http = require('http');
var cookie = require('cookie');
http.createServer(function(request,response){
console.log('cookie: ',request.headers.cookie);
var cookies = {};
if(request.headers.cookie !== undefined){
cookies = cookie.parse(request.headers.cookie);// 쿠키값이 없으면 undefined가뜨고 parse()는 undefiend를 수용하지 못함
}
console.log('cookies: ',cookies.yummy_cookie);
response.writeHead(200,{
'Set-Cookie' : ['yummy_cookie=choco',
'tasty_cookie=strawberry',
`Permanent=cookies; Max-Age=${60*60*24*30}`,
'Secure=Secure; Secure',
'HttpOnly=HttpOnly; HttpOnly',
'Path=Path; Path=/cookie',
'Domain=Domain; Domain=test.o2.org'
]
});
response.end('Cookie!!');
}).listen(3000);
main.js
var http = require('http');
var fs = require('fs');
var url = require('url');
var qs = require('querystring');
var template = require('./lib/template');
var path = require('path');
var sanitizeHtml = require('sanitize-html');
var cookie = require('cookie');
function authIsOwner(request,response){
var isOwner = false;
var cookies ={};
if(request.headers.cookie){
cookies = cookie.parse(request.headers.cookie);
}
if(cookies.email === 'egoing777@gmail.com' && cookies.password === '111111'){
isOwner = true;
}
console.log(cookies);
console.log('isOwner: ',isOwner);
return isOwner;
}
function authStatusUI(request,response){
var authStatusUI = '<a href="/login">login</a>';
if(authIsOwner(request,response)){
authStatusUI = '<a href="/logout_process">logout</a>'
}
return authStatusUI;
}
var app = http.createServer(function(request,response){
var _url = request.url;
var queryData = url.parse(_url,true).query;
var pathname = url.parse(_url,true).pathname
console.log('authStatusUI: ',authStatusUI);
if(pathname =='/'){
if(queryData.id ===undefined){
fs.readdir('./data', function(error,filelist){
var title ='Welcome';
var description = 'Hello, Nodejs';
var list = template.list(filelist);
var html = template.html(title,list,
`<h2>${title}</h2><p>${description}</p>`,
`<a href="/create">create</a> `,
authStatusUI(request,response));
response.writeHead(200);
response.end(html);
})
}else{
fs.readdir('./data', function(error,filelist){
var filteredId = path.parse(queryData.id).base;
fs.readFile(`data/${filteredId}`,'utf8',function(err,description){
var title = queryData.id;
var sanitizedTitle = sanitizeHtml(title);
var sanitizedDescription = sanitizeHtml(description,{
allowedTags : ['h1']
});
var list = template.list(filelist);
var html = template.html(sanitizedTitle,list,
`<h2>${sanitizedTitle}</h2><p>${sanitizedDescription}</p>`,
`<a href="/create">create</a>
<a href="/update?id=${sanitizedTitle}">update</a>
<form action="delete_process" method="post">
<input type="hidden" name="id" value="${sanitizedTitle}">
<input type="submit" value="delete">
</form>
`,
authStatusUI(request,response)
);
response.writeHead(200);
response.end(html);
})
})
}
}else if(pathname=='/create'){
if(authIsOwner(request,response)===false){
response.end('Login required!!')
return false;
}
fs.readdir('./data', function(error,filelist){
var title ='WEB - create';
var list = template.list(filelist);
var html = template.html(title,list,`
<form action="/create_process" method="post">
<p><input type="text" name="title" placeholder="title"></p>
<p>
<textarea name="description" placeholder="description"></textarea>
</p>
<p>
<input type="submit" name="submit">
</p>
</form>`
,''
,authStatusUI(request,response));
response.writeHead(200);
response.end(html);
})
}else if(pathname=='/create_process'){
if(authIsOwner(request,response)===false){
response.end('Login required!!')
return false;
}
var body = '';
request.on('data', function(data){
body += data;
//Too much POST data, kill the connection !
// 1e6 === 1 * Math.pow(10,6) === 1 * 1000000 ~~~ 1MB
/* 용량이 너무 크면 접속을 끊는 소스
if(body.length > 1e6){
request.connection.destroy();
}
*/
});
request.on('end', function(){ // data가 조각조각 전송되다가 더 이상 전송될 데이터가 없으면 호출 됨.(callback함수) 정보수신이 끝났다고 볼 수 있음.
var post = qs.parse(body);
var title = post.title;
var description = post.description;
fs.writeFile(`data/${title}`,description,'utf8',(err) => {
response.writeHead(302,{Location: `/?id=${title}`});
response.end();
})
});
}else if(pathname =='/update'){
if(authIsOwner(request,response)===false){
response.end('Login required!!')
return false;
}
fs.readdir('./data', function(error,filelist){
var filteredId = path.parse(queryData.id).base;
fs.readFile(`data/${filteredId}`,'utf8',function(err,description){
var title = queryData.id;
var list = template.list(filelist);
var html = template.html(title,list,
`
<form action="/update_process" method="post">
<input type="hidden" name="id" value="${title}">
<p><input type="text" name="title" placeholder="title" value="${title}"></p>
<p>
<textarea name="description" placeholder="description" >${description}</textarea>
</p>
<p>
<input type="submit" name="submit">
</p>
</form>
`,
`<a href="/create">create</a> <a href="/update?id=${title}">update</a>`,
authStatusUI(request,response)
);
response.writeHead(200);
response.end(html);
})
})
} else if(pathname =='/update_process'){
if(authIsOwner(request,response)===false){
response.end('Login required!!')
return false;
}
var body = '';
request.on('data', function(data){
body += data;
});
request.on('end', function(){
var post = qs.parse(body);
var title = post.title;
var id = post.id;
var filteredId = path.parse(id).base;
var description = post.description;
fs.rename(`data/${filteredId}`, `data/${title}`, function(err){
fs.writeFile(`data/${title}`,description,'utf8',(err) => {
response.writeHead(302,{Location: `/?id=${title}`});
response.end();
})
})
});
} else if(pathname =='/delete_process'){
if(authIsOwner(request,response)===false){
response.end('Login required!!')
return false;
}
var body = '';
request.on('data', function(data){
body += data;
});
request.on('end', function(){
var post = qs.parse(body);
var id = post.id;
var filteredId = path.parse(id).base;
fs.unlink(`data/${filteredId}`,(err) => {
//삭제가 끝나면 홈으로
response.writeHead(302,{Location: `/`});
response.end();
})
});
}else if(pathname ==='/login'){
fs.readdir('./data', function(error,filelist){
var title ='Login';
var list = template.list(filelist);
var html = template.html(title,list,
`
<form action="login_process" method="post">
<p><input type="text" name="email" placeholder="email"></P>
<p><input type="password" name="password" placeholder="password"></P>
<p><input type="submit"></P>
</form>
`,
`<a href="/create">create</a> `);
response.writeHead(200);
response.end(html);
})
}else if(pathname === '/login_process'){
var body = '';
request.on('data',function(data){
body += data;
});
request.on('end',function(){
var post = qs.parse(body);
if(post.email === 'egoing777@gmail.com' && post.password ==='111111'){
response.writeHead(302,{
'Set-Cookie':[
`email=${post.email}`,
`password=${post.password}`,
`nickname=egoing`
]
,Location: '/'
});
response.end();
}else {
response.end('Who?');
}
})
}else if(pathname === '/logout_process'){
var body = '';
request.on('data',function(data){
body += data;
});
request.on('end',function(){
var post = qs.parse(body);
response.writeHead(302,{
'Set-Cookie':[
`email=; Max-Age=0`,
`password=; Max-Age=0`,
`nickname=; Max-Age=0`
]
,Location: '/'
});
response.end();
})
}else{
response.writeHead(404);
response.end('Not Found');
}
});
app.listen(3000);
인증정보를 쿠키에 저장하는 것은 안전하지 못한 방법이다. 각 사용자의 식별자만 쿠키에 저장하고, 실제 데이터는 서버단에 파일이나 DB에 저장하는
session인증 방식이 보다 안전하다.
쿠키를 이용해서 사용자의 방문 횟수나 언어 설정 등을 기억하는개인화 작업이 가능하다.
쿠키는 4KB 이하로만 전송이 가능하고, localStorage나 Indexed DB를 쓰면 더 많은 데이터를 저장 할 수 있다.
비밀번호 암호화 수단
사용자의 비밀번호를 그대로 DB에 저장시키는 것도 위험하다.
hash, salt, key stretching 등으로 암호화 해야한다.
PBKDF2, bcrypt등의 라이브러리로 가능하다.
Leave a comment