Rubyでセッション管理。

今日はRubyでセッション管理をしたときの覚え書き。

■環境
  • Ubuntu 7.04jp
  • Ruby 1.8.5
  • Apache 2.2.3
  • mod_ruby 1.2.6
■セッション・セッション管理とは

はてなによると
http://d.hatena.ne.jp/keyword/%A5%BB%A5%C3%A5%B7%A5%E7%A5%F3

ユーザがあるサイトを訪れてからそこを離れるまでの一連の通信。
HTTP自体にはユーザを識別する機能が存在しないので、
Cookieなどを使用して同一人物か否かを識別する。
たとえば、はてなにログインしてからログアウトするまでの
すべての操作は一つのセッションと考えられる。


Rubyでは以下のようにセッションを管理する。
Rubyリファレンスマニュアルによると
http://www.ruby-lang.org/ja/man/?cmd=view;name=cgi%2Fsession

cgiライブラリが提供するクッキーを使用してもいいが、
このcgi/sessionを使用した方がよりわかりやすい。
セッション情報はHashライクなインターフェースである。


つまりcgi/sessionライブラリを使えば、Cookieを知らずとも
ハッシュを扱うかのごとくセッションを管理できるということです。

■セッションを使ったカウンタ作成

内容は以下のようなもの。

  1. 初めてページを訪れたらセッションを張る
  2. 次回以降は既存セッションを取得してカウンタ値を増やしていく
#!/usr/bin/ruby

require "cgi"
require "cgi/session"

cgi = CGI.new
begin
  session = CGI::Session.new(cgi,{"new_session"=>false})
rescue ArgumentError
  session = nil
end

if session != nil
  session['count'] = session['count'].to_i + 1
  session.close
else
  session = CGI::Session.new(cgi,{"new_session"=>true})
  session['count'] = 1
  session.close
end

header = cgi.header({"charset"=>"UTF-8"})
puts header
puts "Count is #{session['count']}"


順を追って解説。
まずは例外処理を使ってセッションを取得する。

...
begin
  #既存のセッションを取得。
  #既存のセッションがない場合はArgumentErrorを返す。
  session = CGI::Session.new(cgi,{"new_session"=>false})
#ArgumentError捕捉
rescue ArgumentError
  #明示的にnilを代入(nilを代入しなくても元々nilなはず)
  session = nil
end
...

Session.new(cgi,{"new_session"=>false})はSession.new(cgi)だけでも動作する。
それぞれの違いを説明すると、

  • Session.new(cgi,{"new_session"=>false})
    • 既存セッションがなければ新規セッションを張らず、ArgumentErrorを返す
  • Session.new(cgi)
    • 既存セッションがなけらば新しいセッションを張る

「後者の方が便利じゃないか?」と思うかもしれません。
しかしログイン処理にセッションを利用した場合、認証する前にセッションが
張られてしまいSession Fixationという危険があるそうです(詳しくは参考URLで)。
なので前者を使うようにします。


次にカウント値の初期化orカウントアップ

...
#セッションが張られていれば
if session != nil
  #セッションは文字列しか保存できないので、数値に変換して1足す
  session['count'] = session['count'].to_i + 1
  #closeしてセッション情報をサーバに書き込む
  session.close
#セッションが張られていなければ
else
  #セッションを新規作成してカウント値を初期化
  session = CGI::Session.new(cgi,{"new_session"=>true})
  session['count'] = 1
  #closeしてセッション情報をサーバに書き込む
  session.close
end
...

通常Session#closeメソッドはスクリプト終了時に自動的に実行されます。
(finalizerというのを使っているらしい)
しかしmod_rubyを使う場合は明示的にcloseメソッドを使ってセッションを
クローズしなければなりません。
これはmod_rubyではRubyスクリプト終了したように見えてもApache上で
そのスクリプトを呼び出したmod_rubyモジュールが終了していないからだと思います。
(詳しく知ってる方がいたら教えてください)


最後に表示部分です

...
#セッションを利用する場合はヘッダーを出力しなけらばならない
header = cgi.header({"charset"=>"UTF-8"})
puts header
puts "Count is #{session['count']}"

セッションはCookieを使っているので、かならずヘッダーを出力しなけらばならない。


以上でRubyでのセッション管理の基本は終了。
次回はセッションを利用したログイン処理を作ってみます。
ではでは

■参考

・Session Fixation問題(まちゅダイアリー)
http://www.machu.jp/diary/20060613.html


・Cookie Monster(@IT)
http://www.atmarkit.co.jp/fsecurity/rensai/hoshino06/hoshino01.html


・Ruby CGI::Session(Rubyリファレンス)
http://www.ruby-lang.org/ja/man/?cmd=view;name=cgi%2Fsession


・セッション管理(shugo.net)
http://shugo.net/article/webdb2/#label:13