2010-02-07
Railsノート - セッションまわりを読む (2) - セッションの保存/復元のロジック
途中で力尽きたというか、こういうリーディングのやり方は効率に問題があると悟った(笑)ので、後半ぐだぐだですが、やった分だけうpします。
セッション → Cookie
セッションが Cookie に保存されるロジックから見た方がわかりやすいと思うのでそっちから。CookieStore.call(env) の後半部分(@app.call(env) の後)を見る。
# action_controller/session/cookie_store.rb module ActionController module Session class CookieStore # snip def call(env) env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env) env[ENV_SESSION_OPTIONS_KEY] = @default_options.dup status, headers, body = @app.call(env) session_data = env[ENV_SESSION_KEY] options = env[ENV_SESSION_OPTIONS_KEY] if !session_data.is_a?(AbstractStore::SessionHash) || session_data.send(:loaded?) || options[:expire_after] session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.send(:loaded?) session_data = marshal(session_data.to_hash) raise CookieOverflow if session_data.size > MAX cookie = Hash.new cookie[:value] = session_data unless options[:expire_after].nil? cookie[:expires] = Time.now + options[:expire_after] end cookie = build_cookie(@key, cookie.merge(options)) unless headers[HTTP_SET_COOKIE].blank? headers[HTTP_SET_COOKIE] << "\n#{cookie}" else headers[HTTP_SET_COOKIE] = cookie end end [status, headers, body] end
セッションを Cookie の「値」へと永続化しているのはこの部分↓
session_data = marshal(session_data.to_hash)
marshal の定義は
# Marshal a session hash into safe cookie data. Include an integrity hash.
def marshal(session)
@verifier.generate(persistent_session_id!(session))
end
persistent_session_id! は新規セッションにセッションID を含める処理。クッキーストアの場合、Cookie そのものがセッションなので紐付けのためのセッションID は本来不要なはずだが、コントローラはそんなこと知らない。
@verifier ってなんなんじゃコラ。
def initialize(app, options = {})
# snip
@secret = options.delete(:secret).freeze
@digest = options.delete(:digest) || 'SHA1'
@verifier = verifier_for(@secret, @digest)
# snip
end
# snip
def verifier_for(secret, digest)
key = secret.respond_to?(:call) ? secret.call : secret
ActiveSupport::MessageVerifier.new(key, digest)
end
セッションを Cookie の「値」へと(改竄/偽造予防の措置をとりつつ)永続化するロジックを持っているのがこの ActiveSupport::MessageVerifier というクラス。
ActiveSupport::MessageVerifier
# activesupport/lib/active_support/message_verifier.rb module ActiveSupport class MessageVerifier def initialize(secret, digest = 'SHA1') @secret = secret @digest = digest end # snip def generate(value) data = ActiveSupport::Base64.encode64s(Marshal.dump(value)) "#{data}--#{generate_digest(data)}" end # snip def generate_digest(data) require 'openssl' unless defined?(OpenSSL) OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(@digest), @secret, data) end end end
この辺は以下のエントリで解説されている通りだ。*1
- 2.0のcookie session storeを体感する - ザリガニが見ていた...。
- Rails 2.0の新しいセッション管理-CookieStore - iビジネス&テクノロジー
- Rails 2.0のセッション話 - moroの日記
Cookie → セッション
めんどくさいので省略*2 → まとめ参照
まとめ
ファジーなまとめ。
AbstractStore::SessionHash ↓ marshal ↓ ActiveSupport::MessageVerifier#generate → generate_digest ↓ Cookie ↓ unmarshal ActiveSupport::MessageVerifier#verify → secure_compare ↓ AbstractStore::SessionHash
トラックバック - http://d.hatena.ne.jp/h1mesuke/20100207/p1
リンク元
- 8 http://www.google.co.jp/search?hl=ja&client=firefox-a&rls=org.mozilla:ja:official&tbo=1&num=30&tbs=qdr:y&q=greasemonkey+キャッシュ+クリア&btnG=検索&lr=&aq=f&oq=
- 5 http://reader.livedoor.com/reader/
- 4 http://www.google.co.jp/hws/search?br=&client=fenrir&safe=off&adsafe=off&hl=ja&lr=lang_ja&ie=UTF-8&oe=UTF-8&q=window.Base.class id=
- 2 http://www.google.co.jp/search?hl=ja&source=hp&q=ListControl+セル単位+Aligen&lr=&aq=f&oq=
- 2 http://www.google.co.jp/search?source=ig&hl=ja&rlz=1G1GGLQ_JAJP290&=&q=ActionController+initialize&btnG=Google+検索&meta=lr=&aq=f&oq=
- 2 http://www.google.com/search?hl=ja&client=safari&rls=en&q=rails+https+cookie&btnG=検索&lr=lang_ja&aq=f&oq=
- 1 http://74.125.153.132/search?q=cache:hoB_AeTc0IYJ:d.hatena.ne.jp/h1mesuke/20090505/p1+wxRuby+panel&cd=2&hl=ja&ct=clnk&gl=jp
- 1 http://b.hatena.ne.jp/keyword/ruby?sort=eid
- 1 http://blogsearch.google.co.jp/blogsearch?hl=ja&ie=UTF-8&q=rails&lr=lang_ja&sa=N&start=10
- 1 http://d.hatena.ne.jp/efcl/20100108/1262952103
