zoukankan      html  css  js  c++  java
  • nodejs随记02

    Basic认证

    • 检查报文头中Authorization字段,由认证方式和加密值构成;
    • basic认证中,加密值为username:password,然后进行Base64编码构成;
    • 获取username和password;
    var auth = req.headers['authorization'];
    var encoded = auth.split(' ')[1]; //获取加密值
    var decoded = new Buffer(encoded, 'base64').toString('utf-8').split(':'); //解密
    var user = decoded[0];
    var pass = decoded[1];
    
    • 加密
    var encode = function (username, password) {
      return new Buffer(username + ':' + password).toString('base64');
    };
    
    • 判断认证失败后应该返回401状态码;
    res.setHeader('WWW-Authenticate', 'Basic realm="secure Area"');
    res.writeHead(401);
    
    • 这种认证方式几乎明文,一般只在https情况下使用;

    获得req.body

    • 通过报头transfer-encodingcontent-length判断请求有无带内容
    return 'transfer-encoding' in req.headers || 'content-length' in req.headers;
    
    • 报文内容处理
    var buffers = [];
    req.on('data', function (chunk) {
      buffers.push(chunk);
    });
    req.on('end', function () {
      req.body = Buffer.concat(buffers).toString();
    });
    
    • 内存限制
      • 限制上传内容大小,超过限制停止接受数据并响应400状态码;
    //通过content-length判断
     var bytes = 20;
     var cLength = req.headers['content-length'];
     var len = cLength ? parseInt(cLength, 10) : null;
     if (len && len > bytes) {}
    
     //在接受数据时判断
     var received = 0;
     req.on('data', function (chunk) {
       received += chunk.length;
       if(received > bytes) {
         //停止接受数据,触发end();
            req.destroy();
        }
      }); 
    

    设置缓存

    判断if-modified-since字段

    • fs.stat(path, callback): 获取文件信息
    • stat.mtime.toUTCString() === req.headers['if-modified-since']
      • 相等的话响应304状态码
      • 否则返回文件并重新设置
    var lastModified = stat.mtime.toUTCString();
    res.setHeader('Last-Modified', lastModified);
    
    • 缺陷
      • 文件时间戳改变但内容并不一定改动;
      • 时间戳只精确到秒;

    使用ETag

    • If-Modified-Since/Last-Modified不同,它的请求是If-None-Match/ETag
    • 比较文件加密后的hash值和if-none-match
    //获取hash值
    var getHash = function (str) {
      var shasum = crypto.createHash('sha1');
      return shasum.update(str).digest('base64');
    };
    .....
    var hash = getHash(file); //修改文件后hash值回改变
    var noneMatch = req.headers['if-none-match'];
    if (hash === noneMatch) {
      res.writeHead(304, 'Not Modified');
      res.end();
    } else {
      res.setHeader('ETag', hash);
      res.writeHead(200, 'ok');
      res.end(file);
    }
    

    Expires和Cache-Control头

    • 前面两个方法都还是需要发起一个条件请求
    • Expires
      • 是一个CMT格式的时间字符串;
      • 其缺陷在于浏览器和服务器之间时间可能会不一样;
    var expiresTime = 1000 * 60;
    var expires = new Date();
    ....
    expires.setTime(expires.getTime() + expiresTime);
    res.setHeader('Expires', expires.toUTCString());
    
    • Cache-Control
      • 采用倒计时的方法;
      • 由于HTTP1.0不支持,一般多同时使用expires和cache-control;
      • 若浏览器中同时存在且支持两个值,max-age会覆盖expires;
    var cacheTime = 1000 * 60 * 60;
    ....
    res.setHeader('Cache-Control', 'max-age=' + cacheTime);
    

    清除缓存

    • 路径中跟随应用版本号:http://url.com/?v=20150618;
    • 路径中跟随文件内容hash值: http://url.com/?hash=adddfgg;
    • 一般使用hash更高效;

    cookie和session

    • 对象化cookie
    function parseCookie (cookie) {
      var cookies = {};
      if(!cookie) return cookies;
      var list = cookie.split(';');
      for(var i = 0, len = list.length; i < len; i++) {
        var pair = list[i].split('=');
        cookies[pair[0].trim()] = pair[1];
      }
      return cookies;
    };
    
    • 序列化(字符串化)cookie
    function serializeCoolie (name, val, opt) {
      var pairs = [name + '=' + encodeURIComponent(val)];
      opt = opt || {};
      if (opt.maxAge) pairs.push('Max-Age=' + opt.maxAge);  
      if (opt.domain) pairs.push('Domain=' + opt.domain);
      if (opt.expires) pairs.push('Expires=' + opt.expires.toUTCString());
      if (opt.httpOnly) pairs.push('httpOnly');
      if (opt.secure) pairs.push('Secure');
      return pairs.join('; '); 
    };
    
    • 例子
    http.createServer(function (req, res) {
      req.cookies = parseCookie(req.headers.cookie);
      var num = req.cookies.num || 0;
      num = Number(num) + 1;
      if(!req.cookies.isVisit) {
        res.setHeader('Set-Cookie', [
          serializeCoolie('isVisit', '1', {maxAge: 10}),
          serializeCoolie('num', num)
        ]);
        res.writeHead(200);
        res.write(num + ' ;');
        res.end('first visit!');
      } else {
        res.setHeader('Set-Cookie', serializeCoolie('num', num));
        res.writeHead(200);
        res.write(num + ' ;');
        res.end('welcome again!');
      }
    }).listen(3000);
    

    加密(crypto)

    • 介绍: 提供了加密、解密、签名、验证等功能,利用OpenSSL库来实现加密技术

    Hash算法:

    • 哈希算法,将任意长度的二进制值映射为较短的固定长度的二进制值; 一般对登陆密码,都是使用Hash算法进行加密;

    • 类型:

      • md5
      • sha
      • sha1
      • sha512
      • .....
    • crypto.createHash(algorithm);

    • hash.update(data, [input_encoding])

      • 更新创建的hash内容;
      • 对字符串,input_encoding可以是utf8, ascii,binary,默认binary, 如果是buffer则忽略;
    • hash.digest([encoding])

      • 计算出hash值;
      • encoding可以是hex,binary, base64,这时返回字符串;不设encoding返回buffer;
      • 注意执行之后就必须重新update值;
    //hash:md5
    var md5 = crypto.createHash('md5');
    md5.update('foo');
    md5.update('bar');
    md5.digest('hex');
    
    //hash:sha
    var sha1 = crypto.createHash('sha1');
    sha1.update('foobar');
    sha1.digest('hex');
    

    Hmac算法:

    • 利用哈希算法,以一个密钥和一个消息为输入,生成一个消息摘要作为输出
    • 生成 key.pen: openssl genrsa -out key.pem 1024;
    • crypto.createHmac(algorithm, key)

    加密和解密

    • 对于有安全性要求的数据来说,需要加密存储,然后解密使用的,这时需要可逆的加密算法。
    • 对称加密算法: 一方用KEK加密明文,另一方收到后用同样的KEY来解密。
    • 不对称加密算法: 使用完全不同但又是完全匹配的公钥和私钥。
    • c4aes-256-cbc加密和解密时间都比较短; 如果业务上,解密次数远大于加密次数,最好找到,加密时间:解密时间=N:1,N>1的算法;反之,那么aes-256-cbc算法的计算时间比例就非常适合。
    var crypto = require('crypto');
    var fs = require('fs');
    //加密解密算法
    
      //加密
    function cipher(algorithm, key, buf ,cb){
      var encrypted = "";
      var cip = crypto.createCipher(algorithm, key);
      encrypted += cip.update(buf, 'binary', 'hex');
      encrypted += cip.final('hex');
      cb(encrypted);
    };
    
    //解密
    function decipher(algorithm, key, encrypted,cb){
      var decrypted = "";
      var decipher = crypto.createDecipher(algorithm, key);
      decrypted += decipher.update(encrypted, 'hex', 'binary');
      decrypted += decipher.final('binary');
      cb(decrypted);
    };
    
    function cipherDecipherFile(filename, algorithm, key){
      fs.readFile(filename, "utf-8",function (err, data) {
        if (err) throw err;
        var s1 = new Date();
        cipher(algorithm, key,data,function(encrypted){
          var s2 = new Date();
          console.log('加密算法:' + algorithm + ',' + (s2 - s1) + 'ms');
          decipher(algorithm, key,encrypted,function(txt){
            var s3 = new Date();
              console.log('解密算法:' + algorithm + ',' + (s3 - s2) + 'ms');
            });
          });
      });
    };
    
    var algs = ['blowfish','aes-256-cbc','cast','des','des3','idea','rc2','rc4','seed']; //常用加密解密算法
    var key = "abc";
    var filename = "readme.md";
    algs.forEach(function(name){
      cipherDecipherFile(filename,name,key);
    });
    
    

    签名和验证

    • 除了对数据加密和解密,还需要判断数据在传输过程中,是否被篡改了。就需要用到签名和验证的算法,利用不对称加密算法,通过私钥进行数字签名,公钥验证数据的真实性。
    • 生成私钥: openssl genrsa -out server.pem 1024
    • 生成公钥: openssl req -key server.pem -new -x509 -out cert.pem
    var crypto = require('crypto');
    var fs = require('fs');
    
    //签名验证算法
    function signer(algorithm,key,data){
      var sign = crypto.createSign(algorithm);
      sign.update(data);
      sig = sign.sign(key, 'hex');
      return sig;
    }
    
    function verify(algorithm,pub,sig,data){
      var verify = crypto.createVerify(algorithm);
      verify.update(data);
      return verify.verify(pubkey, sig, 'hex')
    }
    
    var algorithm = 'RSA-SHA256';
    var data = "abcdef";   //传输的数据
    var privatePem = fs.readFileSync('server.pem');
    var key = privatePem.toString();
    var sig = signer(algorithm,key,data); //数字签名
    
    var publicPem = fs.readFileSync('cert.pem');
    var pubkey = publicPem.toString();
    console.log(verify(algorithm,pubkey,sig,data));         //验证数据,通过公钥、数字签名 =》是原始数据
    console.log(verify(algorithm,pubkey,sig,data + "2"));    //验证数据,通过公钥、数字签名  =》不是原始数据
    

    salt算法

    • 密码任意固定位置插入特定的字符串,让散列后的结果和使用原始密码的散列结果不相符;
    var crypto = require('crypto');
    
    //salt算法
    var md5 = crypto.createHash('md5');
    var txt = "123465";
    
    md5.update(txt);
    console.log(md5.digest('hex'));
    
    md5 = crypto.createHash('md5');
    var salt = "abcdefghijklmnopqrstuvwxyz";
    md5.update(txt + salt);
    console.log(md5.digest('hex'));
    
    
    
    //使用crypto.pbkdf2()函数取代手动加盐,默认会调用hmac算法
    var txt = "123465";
    var salt = "abcdefghijklmnopqrstuvwxyz";
    
    // 生成密文,默认HMAC函数是sha1算法, 生成256位的密文
    crypto.pbkdf2(txt, salt, 4096, 256, function (err,hash) {
      if (err) { throw err; }
      console.log(hash.toString('hex'));
    });
    
    
    //利用随机randomBytes()函数,配合pbkdf2()函数,让每次都是不同的salt
    crypto.randomBytes(128, function (err, salt) {
      if (err) { throw err;}
      salt = salt.toString('hex');
      console.log(salt); //生成salt
    
      crypto.pbkdf2(txt, salt, 4096, 256, function (err,hash) {
        if (err) { throw err; }
        hash = hash.toString('hex');
        console.log(hash);//生成密文
      })
    });
    
  • 相关阅读:
    【Python数据分析】NumPy之数组对象基础
    【Oracle11g】20_函数
    【Word】排版技巧
    cache介绍
    cache verilog实现
    在verilog中使用格雷码
    同步fifo与异步fifo
    AHB总线协议(二)
    Android Handler 消息机制原理解析
    值得推荐的开源C/C++框架和库
  • 原文地址:https://www.cnblogs.com/jinkspeng/p/4860225.html
Copyright © 2011-2022 走看看