Programming/node.js2017.02.02 11:23
Programming/node.js2016.12.20 14:28


Node.js 

const crypto = require("crypto");
const algo = 'aes-128-cbc';
const key = "security_key";
const iv = "iv_key";
const value = 'HelloWorld';
const keyBuffer = new Buffer(crypto.createHash('md5').update(key).digest('hex'),"hex");
const ivBuffer = new Buffer(crypto.createHash('md5').update(iv).digest('hex'),"hex");
const textBuffer = new Buffer(value);

let cipher = crypto.createCipheriv(algo, keyBuffer,ivBuffer); let encrypted = cipher.update(textBuffer); let encryptedFinal = cipher.final(); let encryptedText = encrypted.toString('base64') + encryptedFinal.toString('base64'); console.log(encryptedText); //xr046x2iGy4IDUHUm+p7lA==

let decipher = crypto.createDecipheriv(algo, keyBuffer,ivBuffer); decipher.setAutoPadding(true);//padding 처리가 언어별 달라서 확인필요 let decipheredContent = decipher.update(encryptedText,'base64','utf8'); decipheredContent += decipher.final('utf8'); console.log(decipheredContent); //HelloWorld


C# (Unity3D)

출처 : http://wyseburn.tistory.com/entry/AES-CBC-128BIT-PKCS5-%EC%95%94%EB%B3%B5%ED%98%B8%ED%99%94-1

using UnityEngine; using System; using System.Text; using System.IO; using System.Security.Cryptography; using System.Runtime.Remoting.Metadata.W3cXsd2001; public class Crypto { public static readonly string key = MD5Hash("security_key"); public static readonly string iv = MD5Hash("iv_key"); public static string MD5Hash(string str) { MD5 md5 = new MD5CryptoServiceProvider(); byte[] hash = md5.ComputeHash(Encoding.ASCII.GetBytes(str)); StringBuilder stringBuilder = new StringBuilder(); foreach (byte b in hash) { stringBuilder.AppendFormat("{0:x2}", b); } return stringBuilder.ToString(); } private static byte[] GetBytesFromHexString(string strInput) { byte[] bytArOutput = new byte[] { }; if ((!string.IsNullOrEmpty(strInput)) && strInput.Length % 2 == 0) { SoapHexBinary hexBinary = null; hexBinary = SoapHexBinary.Parse(strInput); bytArOutput = hexBinary.Value; } return bytArOutput; } //AES 암호화 public static string AES128Encrypt(string Input) { try { RijndaelManaged aes = new RijndaelManaged(); //aes.KeySize = 256; //AES256으로 사용시 aes.KeySize = 128; //AES128로 사용시 aes.BlockSize = 128; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; aes.Key = GetBytesFromHexString(key); aes.IV = GetBytesFromHexString(iv); var encrypt = aes.CreateEncryptor(aes.Key, aes.IV); byte[] xBuff = null; using (var ms = new MemoryStream()) { using (var cs = new CryptoStream(ms, encrypt, CryptoStreamMode.Write)) { byte[] xXml = Encoding.UTF8.GetBytes(Input); cs.Write(xXml, 0, xXml.Length); } xBuff = ms.ToArray(); } string Output = Convert.ToBase64String(xBuff); return Output; } catch (Exception ex) { Debug.LogError(ex.Message); return ex.Message; } } //AES 복호화 public static string AES128Decrypt(string Input) { try { RijndaelManaged aes = new RijndaelManaged(); //aes.KeySize = 256; //AES256으로 사용시 aes.KeySize = 128; //AES128로 사용시 aes.BlockSize = 128; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; aes.Key = GetBytesFromHexString(key); aes.IV = GetBytesFromHexString(iv); var decrypt = aes.CreateDecryptor(); byte[] xBuff = null; using (var ms = new MemoryStream()) { using (var cs = new CryptoStream(ms, decrypt, CryptoStreamMode.Write)) { byte[] xXml = Convert.FromBase64String(Input); cs.Write(xXml, 0, xXml.Length); } xBuff = ms.ToArray(); } string Output = Encoding.UTF8.GetString(xBuff); return Output; } catch (Exception ex) { Debug.LogError(ex.Message); return string.Empty; } } }


Posted by 시니^^
Programming/node.js2015.12.08 21:33

https://github.com/felixge/node-mysql


Mysql 모듈 보면 CreatePool 후 Query 방식이 두가지가 있다.


Pooling connections

1번안

var mysql = require('mysql');
var pool  = mysql.createPool({
  connectionLimit : 10,
  host            : 'example.org',
  user            : 'bob',
  password        : 'secret'
});

pool.query('SELECT 1 + 1 AS solution', function(err, rows, fields) {
  if (err) throw err;

  console.log('The solution is: ', rows[0].solution);
});

2번안

var mysql = require('mysql');
var pool  = mysql.createPool(...);

pool.getConnection(function(err, connection) {
  // Use the connection
  connection.query( 'SELECT something FROM sometable', function(err, rows) {
    // And done with the connection.
    connection.release();

    // Don't use the connection here, it has been returned to the pool.
  });
});


차이점을 2번의 경우에는 커넥션을 하나 지정해서 해당 커넥션으로해서 쿼리를 처리하고 마지막에 해당 커넥션을 다사용했다고

반환해주는 방식이다.


단순히 일반 SELECT 쿼리 위주의 API 개발일 경우 1번안이 편할 수 있지만 트랜젹센처리나 INSERT 후 Last Insert Id 값을 가져오거나 Found_rows 같은 처리된 Row의 정보를 가져와야 할때는 2번안으로해서 같은 커넥션으로 처리 되어야지만 제대로 된 값을 가져 올 수 있다.


그리고 2번안의 경우 사용 후에 connection.release 해주지 않을 경우 반환하지 않아서 connection limit가 걸릴 수 있다. 

Posted by 시니^^
Programming/node.js2015.06.25 12:55


node.js v.0.5부터 json 파일 require()로 가져 올 수 있다.

그런데... 형식에 조금만 벗어나도 에러가 발생하는데.. 형식은... 잘 맞추면 되는데요..

문제는....주석을 넣어도 에러가 난다 orz 

( 당연히 다른 라이브러리나 file 읽어서 하는 방법도 있지만 기본 기능을 그대로 쓰고 싶음...)

$ vi test.js
var testJson = require('./test.json');
console.log(testJson);

$ vi test.json
{
    //comment test
    "key1":"test1",
    "key2":"test2",
}
$ node test.js
module.js:485
    throw err;
          ^
SyntaxError: /data/webroot/test.json: Unexpected token /
    at Object.parse (native)
    at Object.Module._extensions..json (module.js:482:27)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at Object.<anonymous> (/data/webroot/test.js:1:78)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)


그래서 구글링 결과!!

http://stackoverflow.com/questions/244777/can-i-comment-a-json-file

{ //코멘트라는 키를 생성해버리면됨
   "_comment": "comment text goes here...",
   "glossary": {
{ //키명을 똑같이 해서 위에 설명을 넣어도될듯
  "api_host" : "The hostname of your API server. You may also specify the port.",
  "api_host" : "hodorhodor.com",

생각의 차이~!! 아래처럼 구분할 수있게 ㅎㅎ 어자피 해당 키는 안쓰면 되니까 ㅎㅎ

$ vi test.json
{
    "*****COMMENT*****" : "test comment",
    "key1":"test1",
    "*****COMMENT*****" : "test comment11",
    "key2":"test2",
    "*****COMMENT*****" : ""
}
$ node test.js
{ '*****COMMENT*****': '', key1: 'test1', key2: 'test2' }


Posted by 시니^^
Programming/node.js2015.06.24 16:45

 JSON Web Token (JWT) 

http://jwt.io/

- Restful API 설계과정에서 토큰 인증방식 고민하다가 보게된 JWT


구조는 아주 심플하고 간단함


1. 토큰 데이터의 구조

 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0MTIzMTIzNTQzNTM0NTY3ODkwIiwibmFtZSI6IkpvaDM0NTM0NTM0NW4gRG9lIiwiYWRtaW4iOnRydWV9.gUpPTlD6M3F264lbRyXa6lat7t1tqoP3MHOwFX1qies


1) 첫번째 인자 서명키 생성 방식에 대한 정보 (header) 

⇒ base64인코딩한 { "alg": "HS256",  "typ": "JWT" }

 : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9


2) 두번째 인자 실제 데이터 정보 (payload)

⇒ base64인코딩한 {  "sub": "1234123123543534567890", "name": "Joh345345345n Doe", "admin": true }

eyJzdWIiOiIxMjM0MTIzMTIzNTQzNTM0NTY3ODkwIiwibmFtZSI6IkpvaDM0NTM0NTM0NW4gRG9lIiwiYWRtaW4iOnRydWV9


3) 세번째 인자 데이터에 대한 무결성 / 변조 방지를 위한 HMAC 

⇒ HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),ServerkeyData)

gUpPTlD6M3F264lbRyXa6lat7t1tqoP3MHOwFX1qies


2. node.js 구현

https://github.com/auth0/node-jsonwebtoken

※ 기타 언어 라이브러리 참고 http://jwt.io/#libraries


1) npm 설치

$ npm install jsonwebtoken
jsonwebtoken@5.0.2 node_modules/jsonwebtoken
└── jws@3.0.0 (jwa@1.0.0, base64url@1.0.4)

2) jwt 토큰 생성 및 실행
var jwt      = require('jsonwebtoken');
var tokenKey = "TEST_KEY11"; //토큰키 서버에서 보관 중요
var payLoad  = {'uid':14554};
var token = jwt.sign(payLoad,tokenKey,{
    algorithm : 'HS256', //"HS256", "HS384", "HS512", "RS256", "RS384", "RS512" default SHA256
    expiresInMinutes : 1440 //expires in 24 hours
});
console.log("token : ", token);


$ node jwt_test.js token : eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjE0NTU0LCJpYXQiOjE0MzUxMzA4NzMsImV4cCI6MTQzNTIxNzI3M30.EWNUjnktCWxlqAAZW2bb0KCj5ftVjpDBocgv2OiypqM


2) jwt 토큰 디코딩

var jwt      = require('jsonwebtoken');
var tokenKey = "TEST_KEY11"; //토큰키 서버에서 보관 중요
var token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1aWQiOjE0NTU0LCJpYXQiOjE0MzUxMzA4NzMsImV4cCI6MTQzNTIxNzI3M30.EWNUjnktCWxlqAAZW2bb0KCj5ftVjpDBocgv2OiypqM';

//비동기처리
jwt.verify(token,tokenKey,function(err,decoded){
    console.log("sync : ", decoded);
});

//동기처리
try {
    var decoded = jwt.verify(token,tokenKey);
    console.log("async : ", decoded);
} catch(err){
    console.log(err);
}


$ node jwt_test_decoded.js
async :  { uid: 14554, iat: 1435130873, exp: 1435217273 }
sync :  { uid: 14554, iat: 1435130873, exp: 1435217273
//실행은 sync가 먼저 되었지만 비동기 방식이므로 더 뒤에 출력됨


참고사이트

https://auth0.com/blog/2014/01/07/angularjs-authentication-with-cookies-vs-token/

http://bcho.tistory.com/999

http://bcho.tistory.com/1000


Posted by 시니^^
Programming/node.js2015.03.25 20:14

restful api 만들려고 하는 데 페이지(URI)별 따로 모듈로 빼서 작업할 때 


routes module.exports 할때 매번 해당 모듈페이지에서 DB커넥션 작업 해야되나 고민하는데...


app.js에서 최초 한번 커넥션풀 유지해서 하는 방법에 대해서 어떻게 하나 예시 찾던 중 해결책 발견


참고사이트 : http://cwbuecheler.com/web/tutorials/2014/restful-web-app-node-express-mongodb/



var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
// Database
var mongo = require('mongoskin');
var db = mongo.db("mongodb://localhost:27017/nodetest2", {native_parser:true});
/ Make our db accessible to our router
app.use(function(req,res,next){
    req.db = db;
    next();
});
app.use('/', routes);
app.use('/users', users);

routes 위에다가 requset 정보에 db 커넥션풀 정보를 포함하게 정의하고 next 실행
그리고 각각의 routes 모듈에 보낼 수 있다
DB 커넥션풀 정보 필요없는 routes는 알아서 제외하면될 듯 



Posted by 시니^^
Programming/node.js2015.03.19 15:18

node.js에서 간략하게 IP기반으로 사용자 (위치) 지역 정보 데이터 분석용으로 GeoIP 라이브러리 있나 찾아봤음


역시 라이브러리 geoip-lite 라고 있음!!


Github : https://github.com/bluesmoon/node-geoip


1. 설치방법

$ npm install geoip-lite


2. 코드작성 및 사용법

$ vi geoip.js
=====================================
var geoip = require('geoip-lite');
var ip = "207.97.227.239";
var geo = geoip.lookup(ip);
console.log(geo);
====================================
$ node geoip.js 
{ range: [ 3479297920, 3479301339 ],
  country: 'US',
  region: 'TX',
  city: 'San Antonio',
  ll: [ 29.4889, -98.3987 ],
  metro: 641 }

※ ll 배열값에서 ll: [<latitude(위도)>, <longitude(경도)>] 순서임!!


3. GeoIP 정보 업데이트방법

※ github보면 geoip.startWatchingDataUpdate(); 함수로도 가능한데 아래 script로 하는게 더 낫은듯함

$ cd /usr/lib/node_modules/geoip-lite
$ npm run-script updatedb

> geoip-lite@1.1.5 updatedb /usr/lib/node_modules/geoip-lite
> node scripts/updatedb.js
Retrieving GeoIPCountryCSV.zip ... DONE
Extracting GeoIPCountryCSV.zip ... DONE
Processing Data (may take a moment) ...
Still working (96813) ... DONE
Retrieving GeoIPv6.csv ... DONE
Processing Data (may take a moment) ... DONE
Retrieving GeoLiteCity-latest.zip ... DONE
Extracting GeoLiteCity-latest.zip ... DONE
Processing Data (may take a moment) ...
Still working (122762) ...
Still working (246870) ...
Still working (368626) ...
Still working (490380) ...
Still working (612413) ...
Still working (734688) ...
Still working (855126) ...
Still working (977268) ...
Still working (1096307) ...
Still working (1217573) ...
Still working (1339167) ...
Still working (1459608) ...
Still working (1580033) ...
Still working (1700173) ...
Still working (1822030) ...
Still working (1943449) ... DONE
Retrieving GeoLiteCityv6.csv ... DONE
Processing Data (may take a moment) ... DONE
Successfully Updated Databases from MaxMind.


※ https://github.com/PaddeK/node-maxmind-db 추가 라이브러리(동일하게 MaxMind DB 기반이긴함) 

Posted by 시니^^

Elasticsearch  node.js를 이용해서 데이터 입력(PUT) 하기


logstash이 잘되어있어서 패턴 잘구성해서 logstash을 이용해도 되지만 logstash패턴 사용하는 법을 좀 봐야되고...

잘 사용법도 모르는 환경에서 계속 서비스 RDBMS에 커넥션 유지하는 것도 부담스러워서 따로 node.js로 짜기로함 


기본적으로 콘솔에서 curl 날리는 예시는 아래와 같음

$ curl -XPUT 'http://localhost:9200/logstash-2015.03.06/access_log/1' -d '{ > "host": "web01", > "http_host": "192.168.1.123", > "clientip": "192.2.3.55", > "timestamp": "Fri Feb 06 2015 14:04:22 GMT+0900 (KST)", > "verb": "GET", > "request": "/css/comm.css", > "httpversion": "1.1", > "response": "304", > "bytes": "0", > "referrer": "http://192.168.1.124/", > "agent": "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17", > "request_time": 0 > }'


등록 결과  아래와 같이 나온걸 볼 수 있다

{"_index":"logstash-2015.03.06","_type":"access_log","_id":"1","_version":1,"created":true}


브라우져에서 head 들어가보면 확인 가능하다.

https://localhost:9200/_plugin/head/


이걸 node.js http라이브러리이용하면 간단하다.

var http = require('http'); function elasticsearchPut(index,type,id,put_data){ var option = { hostname: 'localhost', port: 9200, path: '/'+index+'/'+type+'/'+id, method: 'PUT' }; var req = http.request(option, function(res) { console.log('STATUS: ' + res.statusCode); console.log('HEADERS: ' + JSON.stringify(res.headers)); res.setEncoding('utf8'); res.on('data', function (chunk) { console.log(chunk); }); }); req.on('error', function(e) { console.log('problem with request: ' + e.message); }); // DATA PUT req.write(JSON.stringify(put_data)); req.end(); } var index = 'logstash-2015.03.06'; var type = 'access_log'; var id = 2; var data = { "host": "web01", "http_host": "192.168.1.123", "clientip": "192.2.3.55", "timestamp": "Fri Feb 06 2015 14:04:22 GMT+0900 (KST)", "verb": "GET", "request": "/css/comm.css", "httpversion": "1.1", "response": "304", "bytes": "0", "referrer": "http://192.168.1.124/", "agent": "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.17 (KHTML, like Gecko) Chrome/24.0.1312.56 Safari/537.17", "request_time": 0 }; elasticsearchPut(index,type,id,data);


콘솔에서 실행하면 아래와 같이 결과 메세지를 동일하게 볼 수 있다.

$ node test.js 
STATUS: 201
HEADERS: {"content-type":"application/json; charset=UTF-8","content-length":"91"}
{"_index":"logstash-2015.03.06","_type":"access_log","_id":"2","_version":1,"created":true}


예시로 nginx access_log로 했는데 실제 사용하는데는 이벤트나 구매내역등 실제 서비스로그를 RDBMS에서 주기적으로 가져와서 분석하는 용도로 사용함


추가적 node.js 라이브러리 및 Bulk insert 참고

elasticsearch Bulk insert(put) 대량 데이터 입력하기


Posted by 시니^^
Programming/node.js2015.02.26 21:15

node.js socket.io의 cluster 사용 참고사항


기본적으로 클라이언트가 transports: ['websocket'] 기반으로만 사용한다면 문제가 되지 않는다

하지만 XHR Polling 또는 JSONP Polling 사용시에는 

{"code":1,"message":"Session ID unknown"}

문제가 발생한다

폴링은 여러번 요청을 보내는데 여러 process중에 같은 process로 붙을 수 없어서 Session ID 찾을수 없는 듯하다


그래서

http://socket.io/docs/using-multiple-nodes/ 

메뉴얼 사이트에 보면 제시하는 방법이


1. NginX configuration

node.js에서 제공되는 cluster를 사용하지 않고 NginX를 앞단에 둔다

그리고 upstream 을 통해서 프록시 서버를 둔다 그리고 ip_hash 를 설정해서 같은 ip의 경우 같은 포트(즉같은프로세스)를 보도록 해서 문제를 해결한다

※ node.js socket.io는 데몬을 각각 포트별로 여러개 띄워야된다.


2. Using Node.JS Cluster

추가로 node.js Cluster 사용시에는 https://github.com/indutny/sticky-session 

(나도 아직 테스트 제대로 해보지 않았음)


3. Passing events between nodes

그리고 클러스터 구성시에 유저들간의 메세지(이벤트) 전달은 https://github.com/automattic/socket.io-redis

socket.io-redis이용해서 가능한 방법을 제공해주고있다

테스트해보니까 다른 node 프로세스로 붙어있지만 아래 명령어 실행시 이상없이 잘 동작한다.

socket.broadcast.emit  / 자신제외 전체유저

io.sockets.in(socket_id).emit / 특정유저에게메세지전달 

socket.broadcast.to(room_id).emit / 같은 룸에 입장된 유저에게 메세지전달

추가로 용량 처리가 많을 경우 detect_buffers 옵션을 true 해줘야됨

var redis  = require("redis");
var redisAdapter = require('socket.io-redis');
var pub = redis.createClient("/tmp/redis.sock",{ detect_buffers: true });
var sub = redis.createClient("/tmp/redis.sock",{ detect_buffers: true });
io.adapter( redisAdapter({pubClient: pub, subClient: sub}) );



※ socket.io 서비스 하기 위해서 유저별 socket.id를 관리하는 redis 와 서비스 비지니스 로직에 적절한 cluster 기능을 잘 구성해서 사용할 듯합니다.



해당 이슈로써 https://github.com/Automattic/socket.io/issues/1503 어느정도 방안에 대한 댓글을 볼수있다.


아래와 같은 것들도 있는 듯하다 아무쪼록 서비스 방향에 결정되면 좀더 연구해봐야될듯함

https://github.com/TopCloud/socketcluster

https://devcenter.heroku.com/articles/node-sessions 


Posted by 시니^^
Programming/node.js2015.02.04 11:33

node.js  Net 라이브러리 이용한 TCP 서버만들기


※ 자세한 내용은 아래 메뉴얼 참고

- http://nodejs.org/api/net.html#net_net_createserver_options_connectionlistener


1. Server Code

var net = require('net'),fs = require("fs");
var sockFile = '/tmp/nodejs.sock';

var server = net.createServer(function(client) { 
  console.log('client connected');
  //client.write('hello\r\n'); //클라이언트에게 최초 접속시 데이터전달

  //클라이언트에게 받은 데이터
  client.on('data',function(data){ 
      console.log('client In Data : '+data);
      var sendData = 'hello '+data;
      console.log('client Send Data : '+sendData);
      client.write(sendData); //클라이언트에게 추가로 데이터를 보냄
  });

  client.on('end', function() {
    console.log('client disconnected');
  });

  //client.on('data') 이벤트에서 client.write된 데이터와 클라이언트에서 받은 데이터해서 클라이언트에게 보냄
  //client.pipe(client);
});

server.on('error', function (e) {
    //unix sock 사용시
    if (e.code == 'EADDRINUSE') {
        var clientSocket = new net.Socket();
        // handle error trying to talk to server
        clientSocket.on('error', function(e) { 
             // No other server listening
            if (e.code == 'ECONNREFUSED') { 
                fs.unlinkSync(sockFile);
                 //'listening' listener
                server.listen(sockFile, function() {
                    console.log('server recovered');
                });
            }
        });
        clientSocket.connect({path: sockFile}, function() { 
            console.log('Server running, giving up...');
            process.exit();
        });
    }
});

//server.listen(sockFile, function() { //UNIX domain sockets  사용시 
server.listen(8124, function() { //'listening' listener
    console.log('PID ['+process.pid+'] TCP Server listening');
});


2. Client Code

var net = require('net');

//var client = net.connect({path: '/tmp/nodejs.sock'},function() { // UNIX domain sockets  사용시 
var client = net.connect({port: 8124},function() { //'connect' listener
  console.log('connected to server!');
  var data  = 'world!';
  console.log('serve send data : '+data);
  client.write(data);
});

//서버로 부터 받은 데이터
client.on('data', function(data) {
  console.log('serve get data : '+data.toString());
  client.end();
});

client.on('end', function() {
  console.log('disconnected from server');
});

client.on('error', function(err) {
  console.log(err);
});


Posted by 시니^^

티스토리 툴바