クッキーを使わないでリクエストパラメータ(params)でセッションIDを渡す場合
諸事情でセッションIDがクッキーで渡されなく、リクエストパラメータ(クエリーストリングやpost等)
で渡される場合がたまーにあります。
(外部アプリとの連携をする場合等に発生するケースが多いかなと思います。)
こういう場合はsessionメソッドで:cookie_only=>falseを指定すれば
クッキーだけでなく、リクエストパラメータにある場合でも取得できるはずでした。。
ええ、過去形です。
rails2からは:cookie_only=>falseにしてもリクエストパラメータを参照しないように
変更されてました。。バグ?
問題の箇所
query_extension.rb
class CGI #:nodoc: module QueryExtension # Remove the old initialize_query method before redefining it. remove_method :initialize_query # Neuter CGI parameter parsing. def initialize_query # Fix some strange request environments. env_table['REQUEST_METHOD'] ||= 'GET' # POST assumes missing Content-Type is application/x-www-form-urlencoded. if env_table['CONTENT_TYPE'].blank? && env_table['REQUEST_METHOD'] == 'POST' env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded' end @cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE']) @params = {} end end end
@params = {}としているのが原因です。
ここが実際にセッションIDを特定するのに参照している部分だからです。
標準ライブラリのセッションクラスはセッションIDを特定するのに最初にparamsを、なかったらクッキーを見に行く実装になってます。
なので@paramsが消されるとパラメータで見ることはできなくなってしまいます。
cgi/session.rb
if request.key?(session_key) session_id = request[session_key] session_id = session_id.read if session_id.respond_to?(:read) end unless session_id session_id, = request.cookies[session_key] end unless session_id unless option.fetch('new_session', true) raise ArgumentError, "session_key `%s' should be supplied"%session_key end session_id = create_new_id end
皆さんこの問題にはまっているみたいで特にjpmobileでは
携帯のためクッキーが使えないので常に:cookie_only=>falseを内部的に使用していました。
そのためrails2が出た当初はうまくうごかなかったようです。
すぐにパッチが出て、直ったみたいですが。
とうことで、このパッチを適用すれば大体は解決できます。
trans_sid.rb
unless ::CGI::Session.private_method_defined?(:initialize_without_session_key_fixation) ::CGI::Session.class_eval do alias_method :initialize_without_session_key_fixation, :initialize def initialize(cgi, options = {}) key = options['session_key'] if cgi.cookies[key].empty? session_id = (CGI.parse(cgi.env_table['RAW_POST_DATA'])[key] rescue nil) || (CGI.parse(ENV['QUERY_STRING'] || cgi.query_string)[key] rescue nil) cgi.params[key] = session_id unless session_id.blank? end initialize_without_session_key_fixation(cgi, options) end end end
大抵は問題ないんですが、私はひっかかりました。。
ファイルアップロード等のmultipartなpostに対応してないみたいですorz
いろいろ対応してみたのですが(railsによるinitialize_queryの上書きをやめたり、rails1のにしてみたり)結局すぐは直し方はわからなかったのであきらめました。
(時間かければなおせそうですが)
なので取り急ぎの対応的には
直接sessionライブラリを使用して取得しちゃいました。
session_options = HashWithIndifferentAccess.new session_options.merge!({ :session_id => params[:session] }).merge!(ActionController::Base.session_options) session_tmp = CGI::Session.new(CGI.new, session_options ) session_tmp[:hoge] = "hogehoge value" session_tmp.update session_tmp.close
(独自クラスをセッションにいれている場合は
ロード時にクラスがないという感じのエラーになる場合がありました。
その場合は手前でHoge.newとかで一度クラスを認識させると問題なくなります。)
multipartに対応しなくていいならjpmobileのパッチ適用後に下記が使えます。
特定のactionのみで、かつ特定のパラメータが来てた場合のみparamsの値をsession_idにする指定の仕方
(例:actionはhogeのみ。paramsにくるsession_idのkeyはsession_id_dayo。paramsのmodeがtestの場合のみ)
session :only=>:hoge, :cookie_only=>false, :session_key => 'session_id_dayo', :if => Proc.new { |req| req.parameters[:mode] == "test" }
実行環境
rails(2.1.2)