セッション


ウェブサイトはHTTPプロトコルに基づいて業務のインタラクションを行い、HTTPプロトコル自体は無状態のプロトコルであるが、実際には多くのサービスロジックがユーザーの行動情報を記録する必要がある.例を挙げます
一つのショッピングサイトでは、お客様は商品の閲覧ページで必要な商品を選び、仮想のショッピングカートに置いています.注文する時は決算ページにジャンプしなければなりません.この一連のユーザー行為の中で、現在のショッピングカートの情報を記録することが大切です.sessionメカニズムはこのような問題を適切に処理することができます.
    ウェブサイトは、通常、ユーザが第1回ログインしたときに1つのSession IDを生成し、Session IDはユーザIDとは異なり、ユーザIDは常に一意であるが、同じユーザがログインして取得したSession IDは一致しない可能性がある.Session IDは、サーバ端末によって生成され、HTTPプロトコルヘッドを介してクライアントに戻ることにより、可能性のあるセキュリティ問題(他のハイジャックはともかく)を大幅に低減することができる.ブラウザが取得したのは一つのtokenだけなので、これに関するデータはサーバ側で保存されます.サービス端末は、session IDを設定するとともに、session idに変更された期限切れ時間と、session idによって試用された要求パス(path)を指定する.sessionの概略原理を理解し、以下、コードに関連して大まかな使用プロセスを理解する.
クッキーから話します
上記の使用例は、cookieという偉大な発明によるもので、一般的に、クッキーの処理は、次のようないくつかのステップに分けられている.+1.ユーザがウェブページを開く+2.サービス端末でクッキーデータを生成してブラウザ+3に戻す.ブラウザはクッキーを保存+4.その後、ページをロードするたびに、ブラウザはクッキーデータをサーバに送信する.
cookiesの基本データフォーマット
標準HTTPプロトコルは、cookieデータを示すためのフィールドSet-Cookieを有する.サーバは、このフィールドを通じてクライアントクッキーデータを通知する.cookieデータを設定しながら、データにいくつかの構成情報を指定することをサポートしています.-Path:このクッキーデータが影響する経路を表しています.現在のアクセスのurlがマッチを満たしていない場合は、クッキーデータを送信しません.Expires:cookiesデータの期限切れ時間(具体的な時点)は、このオプションを設定しないと、ブラウザが閉じているときにcookieが失われます.このフィールドはUTC形式の文字列です.Max-Age:ブラウザcookieがどれぐらいで期限が切れるかを通知します.クライアントとサーバの時間が一致しないため、期限が切れる時間が正確ではないという問題を解決できます.express内の時間単位はミリ秒です.HttpOnly:cookieは、ブラウザAPIの形式によって、例えばdocument.cookieによって修正することができないことをブラウザに通知する.document.cookieでは見えません.-Secure:この値がtrueに設定されている場合、cookieデータはHTTPプロトコルで無効となり、HTTPSプロトコルでのみ有効となる.domain:domain属性は、cookieが所属するドメイン名を指定しています(Domain).例えば設定domain=example.comは、ブラウザがwww.example.comに、www.abc.example.comが要求を送信するときに、HTTPを持参します.この属性がサーバに設定されていない場合、ブラウザは、cookieに属するドメイン名からデフォルトでcookieに属する.ここで言えば、domainを他のサイトに設定すれば、他のサイトのwww.example.comデータを簡単に変更できますか?ブラウザには、cookieのサーバ設定foo.example.comdomainまたはexample.comであることができるポリシーがある.しかし、foo.example.comdomain ETCに設定することはできません.同時に、セキュリティ上の要因を考慮して、ブラウザは、bar.example.comにのみパブリック(public suffixes)を設定することを拒否し、例えばdomainに設定し、comは許可されていません.
Set-Cookieフォーマット定義>Set-Cookie:name=value;Path:/;Dommin=.domin.comSecure=true
第1の部分は設定されたco.ukキー/値を表し、後にいくつかの構成パラメータが続く.
Cookie解析cookieには、Express/Connectの中間部品がcookieParserの解析に特化している.その実現原理は以下の通りである.


function parserCookie(cookie, options) {
   var obj = {};
   var pairs = cookies.split(/; /);
   var decode = opt.decode || decodeURIComponent;

   pairs.forEach(function(pair) {
      var eq_idx = pair.indexOf('=');
      var key = pair.substr(0, eq_idx).trim();
      var val = pair.substr(eq_idx, pair.length).trim();
       try {
           obj[key] = decode(val);
        }
        catch(err) {
           obj[key] = val;
        }
   });

   return obj;
}

function parseSignCookie(cookies, secret) {
  var keys = Object.keys(cookies);
  var dec = null;
  var ret = Object.create(null);
  var val = null

  for(var i = 0; i < keys.length; i++) {
     var k = keys[i];
     var v = cookies[k];
     dec = unsign(v, secret);
    ret[k] = dec;
  }
  return ret;
}

function parseJSONCookie(cookie) {
   var keys = Object.keys(cookie);
   var key;
   var val;

   for(var i = 0; i < cookie.length; i++) {
      val = cookie[i];
      key = keys[i];
      obj[key] = JSON.parse(val);
   }
}


var cookieParser = function(secret, opts) {
function(req, res, next) {
    if(req.cookies) return;

    var cookies = req.headers.cookies;
    req.cookies = {};
    req.signCookies = {};

   // name=value           json  
    req.cookies = parserCookie(cookies, options);

    if(secret) {
      //      cookie  
      req.signCookies = parseSignCookie(req.cookies, secret);
      //  stringify  JSON  
      req.signCookies = parseJSONCookie(req.signCookies);
    }

    req.cookies = parseJSONCookies(req.cookies);
}
}
Expressは、異なるcookieフィールド値に異なるラベルを付け、cookieParserはこの値がJSONオブジェクトのstrigifyの値であることを示し、j:はこのフィールドが暗号化されていることを示している.デフォルトでは、暗号化されていないフィールドはそれぞれs:req.signCookiesに置かれています.
Cookieの設定req.cookiesは、Expressインターフェースを提供して、res.cookieデータを設定します.Cookieの処理コードを見てみよう.

res.cookie = function (name, value, options) {
  var opts = merge({}, options);
  var secret = this.req.secret;
  var signed = opts.signed;

  if (signed && !secret) {
    throw new Error('cookieParser("secret") required for signed cookies');
  }

  var val = typeof value === 'object'
    ? 'j:' + JSON.stringify(value)
    : String(value);

  if (signed) {
    val = 's:' + sign(val, secret);
  }

  if ('maxAge' in opts) {
    opts.expires = new Date(Date.now() + opts.maxAge);
    opts.maxAge /= 1000;
  }

  if (opts.path == null) {
    opts.path = '/';
  }

  this.append('Set-Cookie', cookie.serialize(name, String(val), opts));

  return this;
};

//   cookie      
function serialize(name, val, options) {  
  var enc = opt.encode || encode;  
  var pairs = [name + '=' + enc(val)];  
  if (null != opt.maxAge) {    
    var maxAge = opt.maxAge - 0;    
    if (isNaN(maxAge)) 
      throw new Error('maxAge should be a Number');    
    pairs.push('Max-Age=' + maxAge);  
  }  
  if (opt.domain) pairs.push('Domain=' + opt.domain);  
  if (opt.path) pairs.push('Path=' + opt.path);  
  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     
res.append = function append(field, val) {
  var prev = this.get(field);
  var value = val;

  if (prev) {
    // concat the new and prev vals
    value = Array.isArray(prev) ? prev.concat(val)
      : Array.isArray(val) ? [prev].concat(val)
      : [prev, val];
  }

  return this.set(field, value);
};