CodeIgniterの学習 7 - キャプチャ(CAPTCHA)を使えるようにする (実験)
(2009/10/02 add)
Captcha Test , Take 2 : Please look the end of this page.
(2009/10/02)
今日はCodeIgniterでキャプチャを使えるようにする。
サンプルアプリを作るのは、実際のコーディング量はたかが知れているので、
先に汎用性のあるプラグインを使いこなせるようにしておく。
キャプチャのプラグインは、system/plugins/captcha_pi.php にはじめから入っていた。
使い方もソースの頭に書いてあった。(マニュアルには無いみたい。)
素直にソースの指示通りに使えるようにしてみる。
以下作業メモ:
1)captchaテーブルを作成する。
CREATE TABLE captcha ( captcha_id bigint(13) unsigned NOT NULL auto_increment, captcha_time int(10) unsigned NOT NULL, ip_address varchar(16) default '0' NOT NULL, word varchar(20) NOT NULL, PRIMARY KEY `captcha_id` (`captcha_id`), KEY `word` (`word`) );
2)サンプルソースを書く。(生成側)
キャプチャの生成部分は、
<?php //(上略) //プラグインの読み込み $this->load->plugin('captcha'); //キャプチャ生成用引数のセット $vals = array( 'img_path' => './captcha/', 'img_url' => 'http://example.com/captcha/' ); //キャプチャを生成 $cap = create_captcha($vals); //キャプチャ確認用にレコードを挿入 $data = array( 'captcha_id' => '', 'captcha_time' => $cap['time'], 'ip_address' => $this->input->ip_address(), 'word' => $cap['word'], ); $query = $this->db->insert_string('captcha', $data); $this->db->query($query); //(下略) ?>
でいけるようだ。(画像は $cap['image'] に img タグ付きで入ってくる。)
$valsの部分は、
<?php $vals = array( 'word' => 'Random word', #文字列 'img_path' => './captcha/',#画像の保存パス(ドキュメントルートから見たパス) 'img_url' => 'http://example.com/captcha/', #画像のURL 'font_path' => './system/fonts/texb.ttf', #使用するフォント 'img_width' => '150', #幅 'img_height' => 30, #高さ 'expiration' => 7200 , #有効期限 ); ?>
のようにいろいろ設定できる。( captcha 画像 の保存先ディレクトリは事前に作成しておく。)
/usr/share/fonts/sazanami-fonts-gothic/sazanami-gothic.ttf を
system/fonts/
にコピーしてttfフォントを使えるようにした。
ついでにフォントサイズが $font_size= 16; となっていて小さすぎるので、
application/plugins/ ディレクトリを作り、captcha_pi.phpをこの中にコピーしてフォントサイズを変更した。
(これくらいなら直接変更してもいいけど、一応流儀に沿った。)
3)サンプルソースを書く。(確認側)
確認側の処理は、
<?php //(上略) //古いキャプチャ確認用データをまず削除(ここでは2時間) $expiration = time()-7200 ; $sql = " DELETE FROM captcha WHERE captcha_time < ? "; $binds = array($expiration); $query = $this->db->query($sql, $binds); //画面から入力された値が正しいかチェック $sql = "SELECT COUNT(*) AS count FROM captcha WHERE word = ? AND ip_address = ? AND captcha_time > ?"; $binds = array($_POST['captcha'], $this->input->ip_address(), $expiration); $query = $this->db->query($sql, $binds); $row = $query->row(); if ($row->count == 0) { //入力値が不正なときの処理 } //(下略) ?>
でいけるらしい。
ソース内のサンプルのままだと一部変数とカラム名に表記揺れがあり、コピペのままだと動かないので部分的に変更した。
実践してみる:
前回までの実験ソースに追記する。(セッション、xajax のテストソース入り)
だんだん読みにくくなってきたけど、
今回追加したのは _hoge4() _hoge5() hoge6() 、
変更したのは index() hoge1()
適当な実験ソースなので美しくないのは勘弁して。
コントローラー側:
application/controllers/tasklist.php<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); // タスクリストというかいろいろ実験 class Tasklist extends Controller { // コンストラクタ function Tasklist() { parent :: Controller(); // urlヘルパ $this -> load -> helper( 'url' ); // xajaxを有効に $this -> load -> library( 'xajax' ); // _hoge3をajax(xajax)のファンクション化する。 // javascript側では xajax__hoge3 で呼び出せる $this -> xajax -> registerFunction( array( '_hoge3', &$this, '_hoge3' ) ); $this -> xajax -> processRequest(); } // デフォルトインデックス function index() { // xajaxの準備 foreach( $this -> _xajax_load() as $k => $v ) { $data[$k] = $v; } // セッションの値確認テスト用 $data["info"] = ( $this -> db_session -> userdata( 'info' ) ) ? $this -> db_session -> userdata( 'info' ) : "中に誰もいませんよ"; // キャプチャタグを生成する $data["cap_img"] = $this -> _hoge4(); $data["cap_msg"] = ""; $this -> load -> view( 'tasklist_view', $data ); } // セッション格納テスト画面 function hoge1() { // xajaxの準備 foreach( $this -> _xajax_load() as $k => $v ) { $data[$k] = $v; } // セッションに値を格納 $this -> db_session -> set_userdata( 'info', 'なんか入ってきた:' . date( "H:i:s", time() ) ); // セッションから取り出し $data["info"] = $this -> db_session -> userdata( 'info' ); // キャプチャタグを生成する $data["cap_img"] = $this -> _hoge4(); $data["cap_msg"] = ""; $this -> load -> view( 'tasklist_view', $data ); } // セッション破棄テスト→リダイレクト function hoge2() { // xajaxの準備 foreach( $this -> _xajax_load() as $k => $v ) { $data[$k] = $v; } // セッションの破棄 $this -> db_session -> sess_destroy(); redirect( '/tasklist' ); } // xajaxのレスポンス用 function _hoge3( $number ) { // xajax用(ボタン押下時のイベント) $objResponse = new xajaxResponse(); // SomeElementId とついたID内をinnerHTML書き換えで、入れ替える。 $objResponse -> Assign( "SomeElementId", "innerHTML", "足し算結果:" . ( $number + 3 ) ); return $objResponse; } // キャプチャの生成用 function _hoge4() { // キャプチャの生成 // プラグインの読み込み $this -> load -> plugin( 'captcha' ); // キャプチャ生成用引数のセット $vals = array( 'img_path' => './img/captcha/', // 画像の保存パス 'img_url' => '/img/captcha/', // 画像のURL 'font_path' => BASEPATH . 'fonts/sazanami-gothic.ttf', // 使用するフォント 'img_width' => '250', // 幅 'img_height' => 60, // 高さ ); // キャプチャを生成 $cap = create_captcha( $vals ); // キャプチャ確認用にレコードを挿入 if ( $cap ) { $data = array( 'captcha_id' => '', 'captcha_time' => $cap['time'], 'ip_address' => $this -> input -> ip_address(), 'word' => $cap['word'] , ); $query = $this -> db -> insert_string( 'captcha', $data ); $this -> db -> query( $query ); } else { return "captchaが生成できないよ" ; } return $cap['image'] ; } // キャプチャの整合性チェック function _hoge5() { // 古いキャプチャ確認用データをまず削除(ここでは2時間) $expiration = time()-7200 ; $sql = " DELETE FROM captcha WHERE captcha_time < ? "; $binds = array( $expiration ); $query = $this -> db -> query( $sql, $binds ); // 画面から入力された値が正しいかチェック $sql = "SELECT COUNT(*) AS count FROM captcha WHERE word = ? AND ip_address = ? AND captcha_time > ?"; $binds = array( $_POST['captcha'], $this -> input -> ip_address(), $expiration ); $query = $this -> db -> query( $sql, $binds ); $row = $query -> row(); if ( $row -> count == 0 ) { return false; } return true; } // キャプチャの確認画面 function hoge6() { // xajaxの準備 foreach( $this -> _xajax_load() as $k => $v ) { $data[$k] = $v; } // セッションの値確認テスト $data["info"] = ( $this -> db_session -> userdata( 'info' ) ) ? $this -> db_session -> userdata( 'info' ) : "中に誰もいませんよ"; // キャプチャタグの整合性チェックと再生成 $data["cap_img"] = $this -> _hoge4(); $data["cap_msg"] = $this -> _hoge5() ? "正解!だけどもう一回" : "残念なのでもう一回"; $this -> load -> view( 'tasklist_view', $data ); } function _xajax_load() { // とりあえずまとめただけ $xajax_js = $this -> xajax -> getJavascript( base_url() ); // _hoge3は、xajax_ をつけて xajax__hoge3(引数) で呼べる // 外部から直接呼ばれないようにファンクション名を // _hoge3としてアンダーバーをつけているので_が2つある $content = '<div id="SomeElementId">下のボタンを押してちょ、中身が書き変わるよ</div>'; $content .= '<p/><input type="button" value="押してみる" onclick="xajax__hoge3(2);">'; return array( "xajax_js" => $xajax_js, "content" => $content ); } } /** * 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> <?=$xajax_js?> <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;} h2 { color: #666; background-color: transparent; border-bottom: 1px solid #ccc ; width:50%; font-size: 14px; font-weight: bold; margin-top:20px;} #txt_red{ color:#dc143c; font-weight: bold;} #msg_red{ color:#dc143c; font-weight: bold; background: #ffbbcc; border:1px solid #ffaacc; width:60%; padding:10px; font-size:1.4em;} #SomeElementId{ color:#ff0000; font-weight: bold; background: #b8e08f; border:1px solid #bfdb58; width:60%; padding:10px; font-size:1.4em;} </style> </head> <body> <h1>Tasklistへようこそ!</h1> <h2>↓セッションの値確認↓</h2> <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> <h2>↓XAJAXのテスト↓</h2> <?=$content?> <h2>↓キャプチャのテスト↓</h2> <form method="post" action="/tasklist/hoge6"> <?=$cap_img?> <p>上に見える文字を素早く入力してください</p> <p id="txt_red"><?=$cap_msg?></p> <input type="text" name="captcha" value="" /> <input type="submit" name="submit" value="★キャプチャのテスト★"> </form> </body> </html>
動作結果
こんな感じ、まあまあかな。一応動いたし。
実装時はまともな条件分岐が必要だけど。
さて、次はログインか。うーんあんまりやりたくないなあ。他のを先にしようかな。
(2009/10/02 add)
Captcha Test , Take 2
Hi! I tried it again.
Simple Example like this .
(original CI 1.7.1 and 'fonts/sazanami-gothic.ttf' : custom font , and mkdir htdocs/captcha )
(Linux-PHP5-Mysql)
(url:http://example.com/example_captcha)
Auto translation may be a bit off (ex1. this to This , ex2. $aaa to $ Aaa).
1. Controller: application/controllers/example_captcha.php
<?php if ( !defined( 'BASEPATH' ) ) exit( 'No direct script access allowed' ); class Example_captcha extends Controller { function Example_captcha() { parent :: Controller(); } function index() { /** * --SQL DDL * * CREATE TABLE captcha ( * captcha_id bigint(13) unsigned NOT NULL auto_increment, * captcha_time int(10) unsigned NOT NULL, * ip_address varchar(16) default '0' NOT NULL, * word varchar(20) NOT NULL, * PRIMARY KEY `captcha_id` (`captcha_id`), * KEY `word` (`word`) * ); */ $captcha_result = ''; $data["cap_img"] = $this -> _make_captcha(); if ( $this -> input -> post( 'submit' ) ) { if ( $this -> _check_capthca() ) { $captcha_result = 'GOOD'; }else { $captcha_result = 'BAD'; } } $data["cap_msg"] = $captcha_result; $this -> load -> view( 'testcaptcha_view', $data ); } function _make_captcha() { $this -> load -> plugin( 'captcha' ); $vals = array( #'img_path' => './captcha/', // PATH for captcha ( *Must mkdir (htdocs)/captcha ) #'img_url' => '/captcha/', // URL for captcha img 'img_path' => dirname(FCPATH).'/captcha/', // PATH for captcha ( *Must mkdir (htdocs)/captcha ) 'img_url' => base_url().'captcha/', // URL for captcha img 'img_width' => 280, // width 'img_height' => 60, // height #'font_path' => BASEPATH . 'fonts/sazanami-gothic.ttf', // custom font ( not in Original CI ) 'expiration' => 7200 , ); // Create captcha $cap = create_captcha( $vals ); // Write to DB if ( $cap ) { $data = array( 'captcha_id' => '', 'captcha_time' => $cap['time'], 'ip_address' => $this -> input -> ip_address(), 'word' => $cap['word'] , ); $query = $this -> db -> insert_string( 'captcha', $data ); $this -> db -> query( $query ); }else { return "Umm captcha not work" ; } return $cap['image'] ; } function _check_capthca() { var_dump( $_POST );//debug // Delete old data ( 2hours) $expiration = time()-7200 ; $this -> db -> where( 'captcha_time < ', $expiration ); $this -> db -> delete( 'captcha' ); var_dump( $this -> db -> last_query() ); // Check input data $this -> db -> select( 'count(*) as count' ); $this -> db -> where( 'word', $this -> input -> post( 'captcha' ) ); $this -> db -> where( 'ip_address', $this -> input -> ip_address() ); $this -> db -> where( 'captcha_time > ', $expiration ); $query = $this -> db -> get( 'captcha' ); $row = $query -> row(); var_dump( $this -> db -> last_query() );//debug if ( $row -> count > 0 ) { return true; } return false; } }//endofclass /** * End of file example_captcha.php */ /** * Location: ./application/controllers/example_captcha.php */ ?>
2 View: ./application/views/testcaptcha_view.php
<html> <head></head> <body> <h2>Captcha Test</h2> <form method="post"> <?php echo $cap_img ;?> <p><?php echo $cap_msg ;?></p> <input type="text" name="captcha" value="" /> <input type="submit" name="submit" value="Submit"> </form> </body> </html>
(2009/10/02 end)