セッション
ウェブサイトは
一つのショッピングサイトでは、お客様は商品の閲覧ページで必要な商品を選び、仮想のショッピングカートに置いています.注文する時は決算ページにジャンプしなければなりません.この一連のユーザー行為の中で、現在のショッピングカートの情報を記録することが大切です.sessionメカニズムはこのような問題を適切に処理することができます.
ウェブサイトは、通常、ユーザが第1回ログインしたときに1つの
クッキーから話します
上記の使用例は、
cookiesの基本データフォーマット
標準
Set-Cookieフォーマット定義>Set-Cookie:name=value;Path:/;Dommin=.domin.comSecure=true
第1の部分は設定された
Cookie解析
Cookieの設定
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.com
がdomain
またはexample.com
であることができるポリシーがある.しかし、foo.example.com
をdomain
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);
};