PHPのRuby on Railsクローンのフレームワーク Akelos PHPをいじってみた出来事のメモ。

Akelosをいじってみるメモ帳

Akelos PHP Blogチュートリアル一覧(全18記事)
Akelos PHP ユーザ認証チュートリアル一覧(全19記事)

2008年09月02日

Akelosユーザ認証チュートリアル 14.メール処理の実装 ActionMailer その1 生成とモデル

Akelos 0.8のダウンロードパッケージには含まれていませんが、svnの最新パッケージを取得すれば、ActionMailer(AkActionMailer)が使えるようになりました。
これで簡単にメール送信のプログラムが作れます。

ActionMailerのインストール方法ですが
http://trac.akelos.org/browser/trunk/

このページからダウンロードして C:\xampp\htdocs\akelos\lib\ に上書きで展開しましょう。。
ダウンロードは画面の一番下の「Download in other formats:Zip Archive」というところから一括ダウンロードできます。

準備が出来たら、メール送信の雛形を作ります。
コマンドラインで以下を入力して実行してください。

C:\xampp\htdocs\projects\blog>c:\xampp\php\php.exe script\generate mailer Notifymail regist remindpassword --sintags --force


generateスクリプトの指定方法は、mailerと指定している以外は、今までと同じ感じですね。
2番目にMailerの名前を指定。
3番目以降に作りたいアクションを書く感じです。

これで関係するファイルが作られます。

なお、8/12現在のものはバグがあり、View用のテンプレートが間違ったディレクトリに作られます。
もし C:\xampp\htdocs\projects\blog\app\views\notifymail\ のディレクトリ内が空の場合は、C:\xampp\htdocs\projects\blog\app\views\app\views\notifymail\ のディレクトリに作られていますので、後者から前者にファイルを移動してください。


続いて、実際のメール送信の処理を書きます。
下記のファイルを開いてください。
C:\xampp\htdocs\projects\blog\app\models\notifymail.php

デフォルトの内容で、registとremindpasswordのアクション内容が入っています。
これを下記のように書き換えます。


<?php

class Notifymail extends AkActionMailer
{

function regist($User)
{
$this->setCharset('ISO-2022-JP');
$this->setRecipients($User->email);
$this->setSubject("[Notifymail] 登録のご案内");
$this->setFrom('master@example.com');
$this->setBody(array( 'User'=> $User ));
$this->log($this->Message);
}


function remindpassword($User)
{
$this->setCharset('ISO-2022-JP');
$this->setRecipients($User->email);
$this->setSubject("[Notifymail] パスワード再発行のご案内");
$this->setFrom('master@example.com');
$this->setBody(array( 'User'=> $User ));

}

}

?>


非常にシンプルですね。
予め用意されている各種メール用のプロパティに必要な情報を与えていくだけです。

最初のsetCharsetは、日本語メールの場合は必須です。
設定しないと標準のUTF-8とかになりますが、文字化けとかおきますので注意。

setRecipients は送信先アドレス。
setSubjetct はメールの題名(タイトル)
setFrom は送信元アドレス
setBody は本文ですが、ここでは引数で渡されてきたUserクラスをそのまま渡しています。Userクラスの各配列がテンプレート内に展開されるようになっています。
posted by AMUAMU at 22:12| Comment(0) | TrackBack(0) | チュートリアル | このブログの読者になる | 更新情報をチェックする

Akelosユーザ認証チュートリアル 13.ApplicationControllerの実装 共通処理

ユーザ認証に関する実際の処理は、色々なコントローラから呼び出される可能性があります。
そこで、どのコントローラから呼び出されても良いように共通のコントローラである「ApplicationController」に実装します。
こうすることで、個別のコントローラは必要な時に必要な処理だけを書けば良いという寸法です。
この便利さは、半端ないのですが、実際には最後のほうで分かると思います。

下記のファイルを開いてください。共通というだけあって、今までのディレクトリより一つ上の階層にありますんで注意。
C:\xampp\htdocs\projects\blog\app\application_controller.php

まず、AccountコントローラのbeforeActionで呼び出す相手となっていた_authpageメソッドを実装しましょう。
以下のコードを加えます。

//各コントローラのbeforeAction登録で呼び出される関数
function _authpage()
{
//セッション情報に有効なデータが入っていれば認証済み
if(!empty($_SESSION['user'])) return true;

//以下ログインしていない場合の処理
$this->flash['notice'] = $this->t('This page needs login.');
//リクエストオブジェクトにあるURIを保存
$_SESSION['return_page'] = $this->Request->env['REQUEST_URI'];
//Userコントローラのログインアクションに移動
$this->redirectTo(array('controller'=>'account','action' => 'login'));
//beforeFilterの処理を進めさせない為、falseを返す
return false;
}

ここでは認証済みかどうかをセッション変数が空かどうかで判断しています。
ログインしていない場合は、今リクエストされた内容をセッションに保存し、Accountコントローラのログインアクションに遷移させています。アプリケーションコントローラは、どのコントローラから呼ばれたか分かりません。よって、redirectToメソッドでは、コントローラも指定していることに注意です。

また、beforeFilterで呼ばれた場合のルールですが、returnでtrue/falseを返す必要があります。
true = そのまま処理を続ける
false = 処理を中断する
これをしないといけません。要注意です。


続いて、同じくAccountコントローラで書かれていたredirectToStoredPageメソッドを足します。
このメソッドは、ログイン完了後、保存してあったアドレスに戻る処理でしたね。
実際、Accountコントローラに記述しても良いのですが、前述の_authpageメソッドで保存先を書いている関係上、同じ並びに書くのが筋です。以下のコードを加えて下さい。

//ログイン後、保存してあったページに戻る関数
function redirectToStoredPage()
{
//念のためログイン済みか確認
if(empty($_SESSION['user'])){
$this->redirectTo(array('controller'=>'account','action' => 'login'));
}else{
if(!empty($_SESSION['return_page'])){
//戻るページの情報を書き戻す
$return_page = $_SESSION['return_page'];
$_SESSION['return_page'] = false;
//保存してあるURIを与えてrecirectToする
$this->redirectTo($return_page);
}else{
//戻るページの指定が無く、単にログインしただけなのでユーザインデックスに戻る
$this->redirectTo(array('controller'=>'account','action'=>'index'));
}
}
}

内容はいたってシンプルです。
細かい処理の流れはコードを見ていただくとして、注目するべきは$this->redirectTo($return_page);でしょうか。redirectToメソッドは結構賢くて、通常は、ここまで出たように配列で飛び先を指定しますが、今回のようにURIアドレスを与えるだけでも解析して飛んでくれます。

最後に、以下のコードを加えます。

//現在のログインユーザの情報(オブジェクト)を返す関数
function currentUser()
{
//View(helper)においてログイン済みかどうかの判断などに使う
return unserialize($_SESSION['user']);
}

コメントにも書いてあるようにこれは、View用の処理です。詳しくはそのうちViewのところの追加説明で使いますので、今はこんなもんってことで。
posted by AMUAMU at 22:00| Comment(0) | TrackBack(0) | チュートリアル | このブログの読者になる | 更新情報をチェックする

Akelosユーザ認証チュートリアル 12.Accountコントローラ 管理者アクション実装

続いて管理者向けの各アクションの実装をします。
これはscaffoldが自動で作ってくれたコードがかなり使えますが、重要なのは管理者権限を持っているかの確認です。ApplicationControllerに実際の管理者権限確認は入れるため、こっちではちょっとだけコードを足すだけです。

管理者向けのアクションlisting、show、add、edit、destroyそれぞれのメソッドの最初に以下のコードを加えます。

//ユーザ情報を書き戻す
$u = unserialize($_SESSION['user']);
//管理者か確認
if(!empty($_SESSION['user']) && empty($u->is_admin)){
$this->redirectTo(array('action'=>'login'));
return;
}


合計5回コピー&ペーストすれば終わりですね、簡単すぎる。scaffoldすばらしー。
ちなみに同じコードが複数出るので、個別の内部関数にすべきかもしれませんが、まぁ今回はめんどくさいので、このままで。


以上で、Accountコントローラの実装は終了です。

念のため追記にコードを全部再掲します。
確認したい人はどうぞ確認用に全部のコードを見る
posted by AMUAMU at 21:54| Comment(0) | TrackBack(0) | チュートリアル | このブログの読者になる | 更新情報をチェックする

Akelosユーザ認証チュートリアル 11.Accountコントローラ アクション実装2

アクション実装の続きです

ユーザ登録削除 deleteアクション
削除は、実際にはデータを消すわけではなくis_enableフラグをfalseに設定し、無効なユーザとしています。実際のコードを示します。
function delete()
{
//ユーザ情報を書き戻す
$u = unserialize($_SESSION['user']);

if(!empty($u->id) && $this->Request->isPost()){
//is_enableフラグをfalseで上書き
$this->user = $this->User->find($u->id);

if($this->user->updateAttribute('is_enable',false)){
$this->flash['notice'] = $this->t('deleted user account');
$this->redirectTo(array('action' => 'index'));
}
}

}

ここではupdateAttributeメソッドにより上書きを指示しています。
似た名前のものとして、 updateAttributesというのもあるので注意です。
どっちも機能は基本手kに同じですが、updateAttributesは複数を、updateAttributeはひとつだけを修正する場合に利用します。
どちらも、呼び出すと上書きとsaveを自動でしてくれます。(内部的には setAttributeして、そのあとsave()しているだけです)

updateAttributeメソッドは、
updateAttribute( '修正したい属性名', '上書きする値','validateによるチェックをするかどうか')
という呼び出しになります。3つめのvalidateチェックのフラグは省略可能です。標準でtrue。つまり確認します。


ユーザ登録情報修正 modifyアクション
登録情報修正は、editアクションに作られたものが流用出来そうですが、修正をユーザに委ねる関係上、結構異なりますので注意。具体的にはid情報を引き回さなくてもSESSIONから取り出しています。
ですが、書き換えを行う時には一回findで取り出す必要がありますので注意。
以下にコードを示します。
//ユーザ登録情報修正
function modify()
{
//ユーザ情報を書き戻す
$u = unserialize($_SESSION['user']);

if(!empty($u->id) && !empty($this->params['user'])){
$this->user =& $this->User->find($u->id);
}else{
$this->user = $u;
$this->user->set('passwd','');
}
//入力された情報で更新を行う
if(!empty($this->params['user'])){
$this->user->setAttributes($this->params['user']);
if($this->Request->isPost() && $this->user->save() ){
$_SESSION['user'] = serialize($this->user);
$this->flash['notice'] = $this->t('updated user profile');
$this->redirectTo(array('action' => 'modify'));
}
}
}

ここではsetAttributesとsaveを別々に利用しています(updateAttributesではなく)。
$this->params['user']にはWebの画面からPOSTされてきたデータが入っています。
その配列データをそのまま読み込ませています。
細かく xxx = yyyy とか書かなくて良いのは楽ですね。
ただし、データの扱い方に心配な時や、入力チェックをここで追加で行う必要があれば、個別展開も考えましょう。validateのほうできちんと処理を書いていれば心配はないですけどね。

setAttributesメソッドはそのまま引数に配列を入れるだけです。
第二引数をつかってオブジェクト指定のupdateとかも出来ますが、複雑なので通常は使わないほうがいいでしょう。

ちなみに、save時に強制的にvalidateされます。要注意。



パスワード忘れ時のパスワード再発行 remindアクション
//パスワード忘れのパスワード再送
function remind ()
{
//セッションを念のため破棄(ユーザ情報不一致が発生するため)
$_SESSION['user'] = false;
if($this->Request->isPost()) {
//ユーザモデルクラスを新たに作る
$u = new User();
//emailアドレスが一致するユーザの情報を探し、Userクラス$uに入れる
$u = & $u->findFirstBy('email AND is_enable', $this->params['user']['email'],true);

//一致するメールアドレスが見つかったかどうかを判断
if( !empty($u->email) ) {
//ユーザモデルに新しいパスワードの発行と更新を依頼
$new_passwd = $u->new_password();

////ここに後でメール処理を追記予定
////詳細はメール機能のところで

$this->flash['notice'] = $this->t('succeeded in the email send of new password.');
$this->redirectTo(array('action'=>'login'));
}else{
$this->flash['notice'] = $this->t('Error! Sorry,plese retry email remind...');
$this->redirectTo(array('action'=>'remind'));
}


}
}

ちょっと長いですね。forgot_passwordメソッドとか長い名前がついているサンプルも多いですが、ここではremindとしています。
ログイン出来ない状態なので、$uにUserモデルを新しく作り、その上で入力されたアドレスから、登録メールアドレスを探す処理をfindFirstByメソッドで行っています。
パスワードの作成ルールはモデルで定義されているので、$u->new_password()で発行し、新しいパスワードを受け取っています。
その後、メールの送信処理を入れるんですが、ここでは入れず、あとでmailerを作ったときに足します。

以上でユーザ向けコントローラの処理は実装終了です。
posted by AMUAMU at 15:57| Comment(0) | TrackBack(0) | チュートリアル | このブログの読者になる | 更新情報をチェックする

Akelosユーザ認証チュートリアル 10.Accountコントローラ アクション実装1

続いてユーザ向けの各アクションの実装をします。

個別のアクション毎に掲出しますが、全てを前述の__constractメソッドの後ろにいれる形になります。

ただし、welcomeだけは中身が必要ありません。
welcomeアクションはbeforeFilterで認証済みユーザのみが入りますが、ここでは各アクションへのリンクを出すだけのページを想定しています。つまりViewだけがあれば良いということですでコントローラはいじりません。
よって、welecomeは追記はありませんので、標準のままです。

その他は色々実装が必要です。アクション別に書いていきます。

機能一覧ページ indexアクション
indexアクションはscaffoldによってlistingへのリダイレクトが書かれています。$this->performAction()または$this->redirectToAction()などが書かれている部分を消します。
そして、セッション情報の書き戻しを加えます。これはユーザ名などをindexページで表示する際に将来必要となるものです。
下記に内容を示します。

function index()
{
// 今回はindexアクションを使うのでリダイレクト無し。よってコメントアウト
// $this->performAction('listing')
$_SESSION['return_page'] ='';

//ユーザ情報を書き戻す
$u = empty($_SESSION['user']) ? false : unserialize($_SESSION['user']);
if(!empty($u)){
$this->user =& $u;
}
}



ユーザ登録 registアクション
ここはscaffoldが作ってくれたaddアクションをコピー&ペーストすると楽です。
addアクションは認証の事を考えていませんし、ユーザ自身が登録するregistアクションの場合、いくつか修正が必要ですので注意してください。以下にコードを示します。

//登録アクション
function regist()
{
//scaffoldが作ったaddアクションからコピー&ペーストで楽に
if(!empty($this->params['user'])){
$this->User->setAttributes($this->params['user']);
if ($this->Request->isPost() && $this->User->save()){
//ここまでは標準状態のaddアクションと同じ。以下、regist用

////ここにはメールの送信処理を後でいれます
///詳細はメール機能のところで

//保存が成功した場合、認証をかける
//さらにセッションにUserモデルクラスをシリアライズして保存
//ログイン状態の維持を行う。

$_SESSION['user'] = serialize($this->User->auth($this->User->email,$this->User->passwd_confirmation));
$this->flash['notice'] = $this->t('account regist success!');
//welcomeメッセージに移動する。

$this->redirectTo(array('action' => 'welcome'));
}else{
//エラーメッセージを足しておきましょう
$this->flash['notice'] = $this->t('Error!! sorry not registed.');
}
}
}

新しいことはありません。ほとんど、ここまでの復習ですね。
細かい流れ、説明はコード中のコメントを参照してください。

少々分かりにくいところは$_SESSIONへの保存ですが、ここではその前に保存したものに対して確認の意味やモデル内のデータをセットしてもらうために、Userモデルのauthを呼んでいます。
authは、成功すればUserモデルのオブジェクト自体を返しますので中身が入ります。失敗すればfalseが入ります。
このように実際の処理はモデルに投げることでコントローラはシンプルになっています。


ユーザログイン loginアクション
ここは全く新しく書きます。以下にコードを示します。

//ログインアクション
function login()
{
if($this->Request->isPost()) {
//ログイン処理を行う。
//認証をかけて、さらにセッションに保存を行う。
$u = $this->User->auth($this->params['user']['email'] , $this->params['user']['passwd']);
if(!empty($u))
{
$_SESSION['user'] = serialize($u);
$this->flash['notice'] = $this->t('login is success');
//保存してあるログイン前の画面に戻るため
//ApplicationControllerのredirectToStoredPage関数に飛ぶ
$this->redirectToStoredPage();
} else {
$_SESSION['user'] = '';
$this->flash['notice'] = $this->t('login failed');
$this->redirectTo(array('action' => 'login'));
}
}
}

ここも同じような処理です。
redirectToStoredPage()メソッドについては、後述のApplicationControllerのところで説明します。


ユーザログアウト logoutアクション
ここも短いですが全く新しいです。コードを示します。

//ログアウトアクション
function logout ()
{
//セッションの情報を破棄
$_SESSION['user'] = false;
$this->flash['notice'] = $this->t('success logout');
//ログイン画面に移動
$this->redirectTo(array('action' => 'login'));
}

シンプルにセッションに保存されているユーザ情報を破棄し、ログイン画面に遷移させています。

続きは次で
posted by AMUAMU at 15:54| Comment(0) | TrackBack(0) | チュートリアル | このブログの読者になる | 更新情報をチェックする

Akelosユーザ認証チュートリアル 9.Accountコントローラの中身 認証確認 beforeFilter

まず最初に、ログインな必要なページの場合、認証済みか確認する処理を加えます。
これにはbeforeFilterという機能を使います。

beforeFilterを使う事で、ページが生成される前に特定の処理をさせることが可能です。
ここでは認証の有無確認をするようにします。
さらにアクション毎に認証済みであることが必要かどうかも、一気に設定できちゃいます。

でわ、その中身としてaccount_controller.phpのAccountControllerクラス内に下記を加えます。

function __construct()
{
// beforeFilterで、自動的にApplicationControllerの_authpageメソッドを呼ぶように指定。
//ただし、delete,logout,editのみが呼び出し対象として指定
$this->beforeFilter(array('_authpage'=>array('only'=>array('delete','logout','modify','add','listing','show','edit','destroy'))));
}


まず、Filter類はクラスの実体化時に呼び出す __construct() メソッド内で設定します。
コメントが入っていますが、実体は一行、というか$this-beforeFilter()メソッドを呼んでいるだけですです。
beforeFilterが指定するのは、ユーザが定義したメソッド(関数)です。
$this->beforeFilter('呼び出す関数名');
で指定できます。これがシンプルな形です。
これによりAccountコントローラが動作する際に必ず呼ばれるようになります。

ここではさらに少々複雑にフィルター条件をつけています。
つまり指定した条件で、特定のアクションのみ、Filterを発動しろと設定しているのです。

$this->beforeFilter(
  array('呼び出す関数名' =>
   array('呼び出す条件'=>
    array('アクション名1','アクション名2',....)
   )
   )
 )

という構造になっています。
呼び出す条件は only または except が指定できます。
only = 指定しているアクションでのみ有効有効にする
except = 指定しているアクション以外で有効にする

ということで、コードに戻ってみますと
呼び出す関数名 _authpage
呼び出す条件 only つまり指定したアクションで有効にする
指定しているアクション 'delete','logout',.....

となっているわけです。

今回は _authpage 関数はアカウントコントローラの中に入れませんでした。
後述するApplicationControllerに入れています。
この理由もApplicationControllerの修正時に説明しますので、とりあえずこの状態で、次へ・・・。
posted by AMUAMU at 13:26| Comment(0) | TrackBack(0) | チュートリアル | このブログの読者になる | 更新情報をチェックする

Akelosユーザ認証チュートリアル 8.Accountコントローラを作る 枠組み自動作成

ユーザモデル続いて、ユーザ登録やログインなどを行うユーザ向けの機能を司るAccountコントローラを作ります。

コマンドラインで以下を入力して実行してください。
C:\xampp\htdocs\projects\blog>c:\xampp\php\php.exe script\generate scaffold User Account regist login logout modify delete remind welcome --sintags --force


後半にアクション(とビュー)の名前を複数指定しています。
このように、アクションがある程度想定して、まとめて作るとある程度自動的に作ってくれるので楽です。もちろん、後で足すことも出来ますが。

アクション名を並べたのは良いですが、もちろん実体はありません。
ということで、それぞれのアクションを下記のように定義し、実体を作りたいと思います。

index = ユーザ向け機能へのリンクページ ログイン必要無し
regist = ユーザ登録 ログイン必要無し
login = ログイン ログイン必要無し
logout = ログアウト ログイン要必要
modify = ユーザ登録情報修正 ログイン要必要
delete = ユーザ登録削除 ログイン要必要
remind = パスワード忘れ時の再発行 ログイン必要無し
welcome = 登録後に表示する画面 ログイン要必要
add = ユーザ追加 管理者用
listing = ユーザ一覧 管理者用
show = ユーザ確認 管理者用
edit = ユーザ登録情報修正 管理者用
destroy = ユーザ削除 管理者用


まず、下記のファイルを開き各アクションが存在することを確認しましょう
C:\xampp\htdocs\projects\blog\app\controllers\account_controller.php

scaffoldが追加指定したアクション+標準で自動で作ってくれる index,listing,show,add,edit,destroyのアクションがあります。

アクションが多いですが、この中を埋めていきたいと思います。
posted by AMUAMU at 03:59| Comment(0) | TrackBack(0) | チュートリアル | このブログの読者になる | 更新情報をチェックする

Akelosユーザ認証チュートリアル 7.Userモデルの確認 完成したUserモデル

ということで、Userモデルが完成しました。念のため、コードの全体を出します。
本当は、ここでテストとかするのかなぁー・・・。まぁ、今回は割愛w


<?php

class User extends ActiveRecord
{
//クラス内の共通暗号化キーです
var $_my_common_salt = 'my_common_pass';

//管理者かどうかのフラグです。
var $is_admin = false;

//管理者として設定する人のメールアドレス
var $_admin_email = array("hoge@sample.com","amuamu@sample.com");

//クラス生成時の処理
function User()
{
// Railsで言うattr_accessorの設定
// DBのテーブルに無い属性を仮想的に作る場合に使います。
// 確認画面などに表示させ、入力させたいフィールドを足すのに必要なものをここでは指定
$this->set('passwd_confirmation');
// Railsで言うattr_protectedの設定
// ユーザが修正出来ないように保護するものを指定
$this->setProtectedAttributes(array('id','salt'));
//親のコンストラクタを呼びます おまじない
parent::__construct(func_get_args());

}

//入力内容のチェック
function validate()
{

//emailはユニークか?
$this->validatesUniquenessOf('email', array('notice'=>$this->t('email address registed!')));

//新しく作るときだけのチェック項目
if($this->isNewRecord()) {

//パスワードと、パスワード確認枠には入力があるか?
$this->validatesPresenceOf(array('passwd','passwd_confirmation'));

//チェックボックスの確認
$this->validatesInclusionOf('is_enable', array('1') ,$this->t('Please check checkbox of confirmation'));
}

//新しく作るとき または 修正時でかつパスワード修正がある場合のみのチェック
if($this->isNewRecord() || !empty($this->passwd))
{
//パスワードの長さチェック
$this->validatesLengthOf('passwd', array('in'=>array(5, 40), 'too_long' => $this->t('password too long.'), 'too_short' => $this->t('password too short')));

//パスワード確認の為の再入力枠とパスワードの内容が一致しているか
$this->validatesConfirmationOf('passwd', $this->t('not confirmation password'));

}

//emailとnameの空入力チェック
$this->validatesPresenceOf(array('email','name'));

//emailアドレスが有効なアドレスか?
$this->validatesFormatOf('email', AK_EMAIL_REGULAR_EXPRESSION, $this->t('error email address.')) ;


}

//データが新規保存される前に呼び出される callback methodです。
//中でパスワードとsaltをセットしています。
function beforeCreate()
{
//パスワードの暗号化関数を呼び出す
$this->encryptPassword();

return true;
}

//データが修正保存される前に呼び出されるcallback methodです。
function beforeUpdate()
{
//パスワードの暗号化関数を呼び出す
$this->_encryptPasswordUnlessEmptyOrUnchanged();
return true;
}

//新しいパスワードを自動生成してセット、保存する関数
//返値には新しいパスワード文(平文)が返される。
function new_password()
{
//新しいパスワードを自動生成(ランダム)8文字の文字列
$new_pass = Ak::randomString(8);

//新しいpassをセット パスワードは平文のまま
$this->set('passwd',$new_pass);

//こっちもセットしないとvalidateで蹴られるんです
$this->set('passwd_confirmation',$new_pass);

//修正した内容で保存。
//なおこの後、beforeUpdateが呼ばれて、平文は暗号化される
if($this->save()) //保存が成功したら新しいパスを返す
return $new_pass;
else
return false;
}

//認証確認用関数
function auth($email,$pass)
{

//emailが一致する行を取り出す、成功すればこのモデルにも反映される
$u =& $this->findFirstBy('is_enable AND email' ,'1',$email);

//見つからなければ$uはfalseが入る。
if(empty($u)) return false;

//見つかった行のパスワードと、入力されたパスワードの一致確認
//保存されているsaltで暗号化してみて、取り出した行と比較
if($this->_myEncrypt($pass,$u->salt) == $u->passwd){

//管理者権限確認と、フラグを立てる
if( array_search($email,$u->_admin_email) !== FALSE ){
$u->is_admin = true;
}else{
$u->is_admin = false;
}

$result = $u;
} else {
$result = false;
}
return $result;
}


//パスワードが修正されている場合だけ暗号化保存をする関数です。
function _encryptPasswordUnlessEmptyOrUnchanged() {
//修正対象のIDの保存されているデータ(つまり旧データ)を呼び出す。
$u =& $this->find($this->id);

//ユーザからのリクエストのpasswdを確認する
switch($this->get('passwd')) {
case '': //空の場合は、保存されてるデータを入れる
$this->set('passwd',$u->passwd);
break;
case $u->passwd: //同じパスワードならばそのまま。
break;
default: //その他の場合はパスワードの修正
$this->encryptPassword();
break;
}

return true;
}


//パスワードを暗号化する関数です。
function encryptPassword()
{
//saltをセットします。
// Akクラスには便利な関数がいくつかあります。
// Ak::randomString() はランダム文字列を得ることが出来ます。
$my_salt = Ak::randomString(8);
$this->set('salt',$my_salt);

//passwdをセットします。
// encryptPassword関数に渡して暗号化してもらいます。
$this->set('passwd', $this->_myEncrypt($this->get('passwd'),$my_salt));
}

//暗号化関数です。
function _myEncrypt($pass,$salt)
{
//sha1で文字列を暗号化しています。
//暗号化文字列は関数引数のsalt+クラス内の固定saltを足しています
return sha1($pass . $this->_my_common_salt . $salt);
}
}
?>

こんな感じで、次号はコントローラへ・・・
posted by AMUAMU at 03:56| Comment(0) | TrackBack(0) | チュートリアル | このブログの読者になる | 更新情報をチェックする

Akelosユーザ認証チュートリアル 6.Userモデルをちゃんと その4 その他関係するメソッド実装

本来、モデルには細かい挙動を実装すべきではないですが、ログインするときに認証する方法と、パスワードをユーザが紛失したときに再発行する仕組みは必要となります。

複雑なシステムの場合は、ここらへんの仕組みは別の認証用クラスとかにまとめる場合もありますが、今回は取りあえずUserモデルに入れてしまいます。

モデルでしか基本的に知らないこと、もしくはモデル以外には教えたくない事を書いています。ここでは、パスワードの暗号化に関する部分を秘匿したい(共通化したい)ので入れます。

下記のようなコードをさらに、クラス内に足して下さい。

//新しいパスワードを自動生成してセット、保存する関数
//返値には新しいパスワード文(平文)が返される。
function new_password()
{
//新しいパスワードを自動生成(ランダム)8文字の文字列
$new_pass = Ak::randomString(8);

//新しいpassをセット パスワードは平文のまま
$this->set('passwd',$new_pass);

//こっちもセットしないとvalidateで蹴られるんです
$this->set('passwd_confirmation',$new_pass);

//修正した内容で保存。
//なおこの後、beforeUpdateが呼ばれて、平文は暗号化される
if($this->save()) //保存が成功したら新しいパスを返す
return $new_pass;
else
return false;
}

//認証確認用関数
function auth($email,$pass)
{

//emailが一致する行を取り出す、成功すればこのモデルにも反映される
$u =& $this->findFirstBy('is_enable AND email' ,'1',@$email);

//見つからなければ$uはfalseが入る。
if(empty($u)) return false;

//見つかった行のパスワードと、入力されたパスワードの一致確認
//保存されているsaltで暗号化してみて、取り出した行と比較
if($this->_myEncrypt($pass,$u->salt) == $u->passwd){

//管理者権限確認と、フラグを立てる
if( array_search($email,$u->_admin_email) !== FALSE ){
$u->is_admin = true;
}else{
$u->is_admin = false;
}

$result = $u;
} else {
$result = false;
}
return $result;
}

これらの関数は、モデルを使うコントローラ側から呼び出される事が想定しています。

よく使うもので新しいメソッドはfindFirstBy()でしょうか。
ルールを指定して、データをDBから探してきてくれます。
emailと指定しているものは、$email変数との一致を見るクエリに自動的に代入されます。あわせてANDでis_enable はtrueであるかどうかを確認させています。
ちなみに@$emailという表現は、関数実行のエラー表示抑制をしてくれるものです。おまじないみたいなもんだと思って下さい。あまりこの@演算子は説明が無いようですが・・・

管理者権限は配列をなめて(array_search)、一致する場合のみadminフラグを立てる単純な処理です。
レベル管理するように改変する場合はここら辺が修正しどころですね。
posted by AMUAMU at 03:52| Comment(0) | TrackBack(0) | チュートリアル | このブログの読者になる | 更新情報をチェックする

Akelosユーザ認証チュートリアル 5.Userモデルをちゃんと その3 beforeCreate/beforeUpdate

続いてパスワードを保存する時に暗号化する処理です。
いくつかのところで実装する方法があるのですが、今回はbeforeCreate()とbeforeUpdate()を使ってみます。
※Rubyなら演算子の再定義(オーバーロード)が、一番スマートなんだろうけどPHPは出来ないんですよねー

ということで、下記のようなコードをvalidateメソッドの後ろに足します。


//データが新規保存される前に呼び出される callback methodです。
//中でパスワードとsaltをセットしています。
function beforeCreate()
{
//パスワードの暗号化関数を呼び出す
$this->encryptPassword();

return true;
}

//データが修正保存される前に呼び出されるcallback methodです。
function beforeUpdate()
{
//パスワードの暗号化関数を呼び出す
$this->_encryptPasswordUnlessEmptyOrUnchanged();
return true;
}


encryptPassword()関数と、_encryptPasswordUnlessEmptyOrUnchanged()関数は後で説明します。

とりあえず、beforeCreateとbeforeUpdateについて。これらはActiveRecordに組み込まれているコールバックメソッドです。コールバックメソッドとは、特定のタイミング(トリガー)で自動的に呼び出される場所のことを言います。フックとか、イベントとかって表現をしているフレームワークもありますね。まぁ、どれも似たようなもんです。

ここで指定している2つは以下の意味があります。
beforeCreate 新しいレコードを保存する前に呼び出される
beforeUpdate レコードを更新・修正する前に呼び出される
です。

これらの前にvalidateもコールバックメソッドとして呼び出されています。
これらの呼び出し順は、決まっていて、その他にもいくつかメソッドがあるんですが、それぞれの説明は今回は割愛
function beforeCreate()
function beforeValidation()
function beforeValidationOnCreate()
function beforeValidationOnUpdate()
function beforeSave()
function beforeUpdate()
function afterUpdate()
function afterValidation()
function afterValidationOnCreate()
function afterValidationOnUpdate()
function afterCreate()
function afterDestroy()
function beforeDestroy()
function afterSave()
とりあえず、こんな種類があると思って下さい。意味は名前でだいたいわかるかと思います。

ひとつ、これらのコールバックでは return true; で返して下さい。returnを返さない or return false;は処理が中断する場合があります。逆に内容によって処理を中断したい場合もあるでしょうけど。

さて、話は戻って、beforeCreateとbeforeUpdateの中は、それぞれ別のユーザ定義のメソッドを呼んでいるだけです。それぞれのメソッドをさらに書き加える必要があります。

下記のようなコードをさらに、クラス内に足して下さい。


//パスワードが修正されている場合だけ暗号化保存をする関数です。
function _encryptPasswordUnlessEmptyOrUnchanged() {
//修正対象のIDの保存されているデータ(つまり旧データ)を呼び出す。
$u =& $this->find($this->id);

//ユーザからのリクエストのpasswdを確認する
switch($this->get('passwd')) {
case '': //空の場合は、保存されてるデータを入れる
$this->set('passwd',$u->passwd);
break;
case $u->passwd: //同じパスワードならばそのまま。
break;
default: //その他の場合はパスワードの修正
$this->encryptPassword();
break;
}

return true;
}


//パスワードを暗号化する関数です。
function encryptPassword()
{
//saltをセットします。
// Akクラスには便利な関数がいくつかあります。
// Ak::randomString() はランダム文字列を得ることが出来ます。
$my_salt = Ak::randomString(8);
$this->set('salt',$my_salt);

//passwdをセットします。
// encryptPassword関数に渡して暗号化してもらいます。
$this->set('passwd', $this->_myEncrypt($this->get('passwd'),$my_salt));
}

//暗号化関数です。
function _myEncrypt($pass,$salt)
{
//sha1で文字列を暗号化しています。
//暗号化文字列は関数引数のsalt+クラス内の固定saltを足しています
return sha1($pass . $this->_my_common_salt . $salt);
}

それぞれの細かい処理はコード内のコメントを参照してください。

よく使うのは $this->set() や$this->get() あたりぐらいでしょうか。
ここではbeforeCreateとbeforeUpdateの前、つまりDBに保存される前に、データを書き換えたり、加えたりしているわけです。
ここではsaltを作ってsetしたり、passwdを暗号化して書き換えたりしていますね
beforeCreateやbeforeUpdateから呼び出される流れを追って確認すると良いと思います。
posted by AMUAMU at 03:45| Comment(0) | TrackBack(0) | チュートリアル | このブログの読者になる | 更新情報をチェックする

広告


この広告は60日以上更新がないブログに表示がされております。

以下のいずれかの方法で非表示にすることが可能です。

・記事の投稿、編集をおこなう
・マイブログの【設定】 > 【広告設定】 より、「60日間更新が無い場合」 の 「広告を表示しない」にチェックを入れて保存する。


×

この広告は1年以上新しい記事の投稿がないブログに表示されております。