CodeIgniterの学習 4 - セッションをDBに保持するようにする (暫定版)1
(08/11/10追記)
http://d.hatena.ne.jp/dix3/20081110/1226243974 にまとめました。
(08/10/14追記 一部変更 ソース内のinput->cookieの読み出し箇所2カ所を
→
$session_id = $this->CI->input->cookie($this->sess_cookie);
にし、xss_cleanにしました 。)
$session_id = $this->CI->input->cookie($this->sess_cookie,true);
(08/10/18追記
http://d.hatena.ne.jp/dix3/20081018/1224275341
にこのエントリの補足事項を追加しました。併せて参照ください。)
(08/11/05追記
Version 1.7.0からは、標準のSession.phpでもセッションデータをDBに保存できる
ようになったみたいです。
Updated the Sessions class so that any custom data being saved gets stored to a database rather than the session cookie (assuming you are using a database to store session data), permitting much more data to be saved.
(意訳)
セッションクラスを更新したよ。
んで、いろいろぶち込みたいカスタムデータをクッキーじゃなくて、データベース側に保存できるようになったのだ。
これで多い日も安心だわ。
(http://codeigniter.com/user_guide/changelog.html より引用)
http://codeigniter.com/user_guide/libraries/sessions.html を参考のこと。
ci_sessionsテーブルのsession_dataのカラム名をuser_dataにリネームすれば、
Version1.7.0のCodeIgniter標準にも移行できるはずです。
あるとすれば、 $this->db_session-> だったところを、$this->session->にする位か。
まあ、それも http://d.hatena.ne.jp/dix3/20081018/1224275341 とかでゴニョゴニョ弄れば、
吸収できるから気にしないでいいや。
俺は以後も取りあえずDB2_Sessionを使うけど、ある程度1.7.0が安定したら標準側も試してみるつもり。
なお以下の記述の概念自体は、1.7.0標準でも同義だと思います。
)
以上追記終わり
(以下エントリー)
CodeIgniterのセッション管理はデフォルトだとCookieにデータを保存する独特な方式をとっている。
前から気になっていたので、これを変更することにした。
セッションの管理方式にはそれぞれメリット・デメリットがあると思うが、
俺の脳内妄想判定での、メリットデメリットは以下の通り
方式1:クッキーにセッションデータを保存する(CI独自のセッション管理)
- メリット:
- サーバ側に負荷が掛からない
- 複数WEBサーバで負荷分散する時も、サーバ側にデータを保存しないからセッション管理が楽
- デメリット:
- クッキーに持たせるのに抵抗あり
- 保存できる長さ制限
- 携帯では無理だったり
- メリット:
- 基本ゆえノウハウがあちこちにある。
- デメリット:
方式3:DB管理方式(libraries追加)
- メリット:
- DBでセッション管理
- 複数WEBサーバで負荷分散しても同一のセッションを見やすい、というか何もしなくていい。
- デメリット:
- DBが必要
- セッション見るたびにDBアクセスが必要なので、大量アクセス系だときつい?
ちなみに10台以上の負荷分散は別次元の話だと思っているんで、よく分からない。あくまで数台の世界で考えてます。
ウチの場合には、正直どれでもいい(専用サーバ & 同時使用人数今後も少なめ)ので、
方式3のDB管理方式 を採用。
ただし、気に入らない点もあったので、暫定的に一部手を入れた。
設置覚え書き:
入手ソース:A) DB_Session - http://codeigniter.com/wiki/DB_Session/
B) DB2_Session - http://codeigniter.com/wiki/DB2_Session/
A)からダウンロードできるソース (http://codeigniter.com/wiki/File:db_sessions.zip/)
をはじめに導入したが、後で改良版のB)を見つけたので、
B)のソース内のクラス名をDB2_Session→Db_session に変えて、これを使っている。
(08/09/29追記・変更 ログイン認証でFreakAuthを使う関係で、ファイル名DB_Session.phpをDb_session.phpにし、クラス名とコンストラクタもDB_SessionをDb_session に変更した。)
設定方法:
http://codeigniter.com/wiki/DB_Session/ を見ればいいだけだが、一応メモちなみにDBは既に設定済み(mysql5を使用)、DBの使用方法はマニュアル見れば分かるので割愛。
- http://codeigniter.com/wiki/File:db_sessions.zip からソースを入手
- 解凍したファイルのDB_Session.phpを、application/libraries/Db_session.phpとして設置する。
- application/config/autoload.php で
$autoload['libraries'] = array('database','Db_session');
にする - A)のリンク先説明内の2/ Configuration を参考に application/config/config.php で
$config['sess_use_database'] = TRUE;
$config['sess_match_ip'] = TRUE;
$config['sess_match_useragent'] = FALSE;
にする。 - A)のリンク先説明内の3/ Database のddl文を流してセッション管理テーブルを作る。
- ここまででもいいのだが、せっかくなので、B)のリンク先のソースをコピーして、Db_session.php内に貼り付ける。ただし、クラス名とコンストラクタのDB2_Session は Db_session に変更する。($this->db_session-> みたいに呼び出したいから)
これで、
<?php //セッションに値を格納 $this->db_session->set_userdata('info', 'なんか入ってきた:' .date("H:i:s",time()) ); //セッションから取り出し $data["info"] = $this->db_session->userdata('info'); //セッションの破棄 $this->db_session->sess_destroy(); ?>
みたいにセッションが扱えるようになる。
これでもいいのだが、セッションの破棄の部分が
<?php //上略 /** * Destroy the current session * * @access public * @return void */ function sess_destroy() { setcookie( $this->sess_cookie, '', ($this->now - 31500000), $this->CI->config->item('cookie_path'), $this->CI->config->item('cookie_domain'), 0 ); } //下略 ?>
としか書いて無くて、セッションを消してもDBにはレコードが残りっぱなしで、
ガーベージコレクション(sess_gc)が呼ばれるまで、ゴミが残り続けて気持ちが悪いので、
<?php //上略 /** * Destroy the current session * * @access public * @return void */ function sess_destroy() { //$session_id = $this->CI->input->cookie($this->sess_cookie);//セッションid取得 $session_id = $this->CI->input->cookie($this->sess_cookie,true);//セッションid取得 xss_cleanのフラグをtrueに if($session_id){ $this->CI->db->where('session_id', $session_id); $this->CI->db->delete($this->session_table);//セッションのレコードを物理削除 } setcookie( $this->sess_cookie, '', ($this->now - 31500000), $this->CI->config->item('cookie_path'), $this->CI->config->item('cookie_domain'), 0 ); } //下略 ?>
みたいに、sess_destroy() 呼び出し時は、速攻で物理削除するようにした。
(08/10/14 追記)
上記に追加したsess_destroy()内と、sess_read()内に、
の箇所があるところを
$session_id = $this->CI->input->cookie($this->sess_cookie);
に変更しxss_cleanをtrueにしてみた。
$session_id = $this->CI->input->cookie($this->sess_cookie,true);
(08/10/16 追記)
ちなみに、session_idは、画面遷移の度(db_sessionがロードされる度)に毎回変更される。
(当然session_dataの値は引き継がれる)
ログイン前→ログイン完了→ログオフするまでずっと同じsession_idではないので、セッション固定化攻撃は受けにくいはず。
もしこの挙動が嫌で、どうしても固定化したい時は、フォーラムの続き(http://codeigniter.com/forums/viewthread/75687/)を参照するとよい。
実験用コントローラーとビューを設置
いろいろなソースの実験用+完成時にタスクリストになるように、
application/controllers/welcome.php をコピーして
application/controllers/tasklist.php を作り、
application/views/welcome_message.phpをコピーして
application/views/tasklist_view.php を作った。
あと mod_rewriteでindex.phpを除去している。
RewriteEngine On RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteCond $1 !^(index\.php|images|img|css|js|robots\.txt) RewriteRule ^(.*)$ /index.php/$1 [L]
application/config/config.php
$config['index_page'] = ""; $config['uri_protocol'] = "REQUEST_URI";
てなかんじ。
動作確認ソース:
application/controllers/tasklist.php<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Tasklist extends Controller { function __construct() { parent::Controller(); } function index() { $data["info"] = ($this->db_session->userdata('info')) ? $this->db_session->userdata('info') : "中に誰もいませんよ"; $this->load->view('tasklist_view',$data); } function hoge1() { //セッションに値を格納 $this->db_session->set_userdata('info', 'なんか入ってきた:' .date("H:i:s",time()) ); //セッションから取り出し $data["info"] = $this->db_session->userdata('info'); $this->load->view('tasklist_view',$data); } function hoge2() { $this->load->helper('url'); //セッションの破棄 $this->db_session->sess_destroy(); redirect('/tasklist'); } } /* End of file tasklist.php */ /* Location: ./application/controllers/tasklist.php */ ?>
application/views/tasklist_view.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Tasklistへようこそ</title> <style type="text/css"> body { background-color: #fff; margin: 40px; font-family: Lucida Grande, Verdana, Sans-serif; font-size: 14px; line-height:26px; color: #4F5155; } a { color: #003399; background-color: transparent; font-weight: normal; text-decoration:underline; } h1 { color: #444; background-color: transparent; border-bottom: 1px solid #D0D0D0; font-size: 16px; font-weight: bold; margin: 24px 0 2px 0; padding: 5px 0 6px 0; } #msg_red{ color:#dc143c; font-weight: bold; background: #ffbbcc; border:1px solid #ffaacc; padding:10px; font-size:1.4em; } </style> </head> <body> <h1>Tasklistへようこそ!</h1> <p>↓セッションの値確認↓</p> <p id="msg_red"><?= $info ?></p> <ul> <li><a href="/tasklist/hoge1"> /tasklist/hoge1 に行ってセッションを格納してみる </a></li> <li><a href="/tasklist/hoge2"> /tasklist/hoge2 でセッションを破棄して/tasklistにリダイレクト </a></li> </ul> </body> </html>
画面も貼っておく
まだまだすごくしょぼい。道のりは長い。さあ、次いってみよう。
次は何しよう。
ああ、あれだ、xajaxかその代替品の組み込みだ。