RailsのSessionでRedisStoreを使用している時
ブラウザのクッキーに保存されるvalueとRedisのkeyとの関係性が全くわからなかったので調査した。
ライブラリはこれ。
Qiitaやドキュメントとかには全然情報がなかった。
調べるのに半日以上使ったので消えないように、その時の内容の書き溜め。
「Redisに保存する!」って記事は結構あったんだけど、
どう紐付いているかについて説明していたり調べている記事がなかったのでソースコード追っかけた。
先に結論から
結論
private_idがredis側に保存されるkeyになる。
public_idはブラウザ側に渡されるvalue
Digest::SHA256.hexdigestでprivate_idを生成している
Redisから直接セッション情報を取得する場合
Redisから直接セッション情報を取得する場合は下記のようになる。
ブラウザのCookievalueを取得したらしっかり参照できた。
1 2 3 4 |
cookie_value = “57f8466e27ada518d54d2442635357b6” key = Digest::SHA256.hexdigest(cookie_value) redis = Redis.new(:host => “redis”, :port => 6379) v = redis.get(key) |
やったぜ。
こっからは興味ある人だけでいいと思います。
調査記録
記載がバラバラなので興味ある人だけ読んでください。
Sessionの取得は上で確認できるので。
private_idがredis側に保存されるkeyになる。
public_idはブラウザ側に渡されるvalue
private_idの実装は下記
1 2 3 4 5 6 |
https://github.com/rack/rack/blob/master/lib/rack/session/abstract/id.rb#L24 # File 'lib/rack/session/abstract/id.rb', line 24 def private_id "#{ID_VERSION}::#{hash_sid(public_id)}" end |
hash_sidというメソッドは下記、
sidというpublic_idを渡してprivate_idを生成するために使っている。
1 2 3 4 5 |
https://github.com/rack/rack/blob/master/lib/rack/session/abstract/id.rb#L36 def hash_sid(sid) Digest::SHA256.hexdigest(sid) end |
RedisActionpackでRack::Session::SessionId.new()をおこなっている。
superで何を渡しているかよくわからないので追っかける必要あり。
1 2 3 4 |
https://github.com/redis-store/redis-actionpack/blob/c9ad62aaec675f2b9a6d8ae691315da6fbdcceec/lib/action_dispatch/middleware/session/redis_store.rb#L21 def generate_sid Rack::Session::SessionId.new(super) end |
generate_unique_sidの中で、
1 2 3 4 5 6 7 8 9 10 11 12 |
https://github.com/redis-store/redis-rack/blob/a833086ba494083b6a384a1a4e58b36573a9165d/lib/rack/session/redis.rb#L22 def generate_unique_sid(session) return generate_sid if session.empty? loop do sid = generate_sid first = with do |c| [*c.setnx(sid.private_id, session, @default_options.to_hash)].first end break sid if [1, true].include?(first) end end |
sessionの再読み込み書き部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'lib/rack/session/abstract/id.rb', line 373 def commit_session(req, res) session = req.get_header RACK_SESSION options = session.options if options[:drop] || options[:renew] session_id = delete_session(req, session.id || generate_sid, options) return unless session_id end return unless commit_session?(req, session, options) session.send(:load!) unless loaded_session?(session) session_id ||= session.id session_data = session.to_hash.delete_if { |k, v| v.nil? } if not data = write_session(req, session_id, session_data, options) req.get_header(RACK_ERRORS).puts("Warning! #{self.class.name} failed to save session. Content dropped.") elsif options[:defer] and not options[:renew] req.get_header(RACK_ERRORS).puts("Deferring cookie for #{session_id}") if $VERBOSE else cookie = Hash.new cookie[:value] = cookie_value(data) cookie[:expires] = Time.now + options[:expire_after] if options[:expire_after] cookie[:expires] = Time.now + options[:max_age] if options[:max_age] if @same_site.respond_to? :call cookie[:same_site] = @same_site.call(req, res) else cookie[:same_site] = @same_site end set_cookie(req, res, cookie.merge!(options)) end end |
つまり、
ブラウザのsessionのvalueを
Digest::SHA256.hexdigest(value)で16進数でSHA256ハッシュ変換すると
redisのkeyにたどり着くわけだ。
大変だった。
じゃあね〜〜。