Zend_Authでユーザー認証&DB2のencryption
Zend_Authを使った、ユーザー認証をやってみたので、メモをしておきます。
IBM i(AS/400)の既存データベースに存在している社員マスターのユーザーIDとパスワード情報で認証を行います。
Zend_Authは、データベース認証用に”Zend_Auth_Adapter_DbTable”というクラスが用意されています。このクラスは、コストラクタに”Zend_Db_Adapter_xxxxxx”のインスタンスを渡す必要があり、今回のケースでは”Zend_Db”を使っていなかったので、”Zend_Auth_Adapter_Interface”を実装して、オリジナルの”Zend_Auth_Adapter”を作成して認証してみました。
まず、社員マスターの認証をチェックするメソッドを実装します。認証の判定としては
-
- 認証成功
- ユーザーが存在しない
- 廃止ユーザーで認証不可
- パスワードのアンマッチ
- その他のエラー
の5つとします。
encryptionを使ったパスワードの暗号化
話はソレるのですが、先日発表されたPower7のIBM i OS 7.1では、データベース列における暗号化が可能になるそうです。しかし、自分が使っている環境のV5R4では、それが実現出来ませんので、DB2のEncryptを使って暗号化したいと思います。
暗号化したデータを保存する為には、DDLで下の様に”varchar for bit”を定義するか、DDSで”H”タイプのフィールド定義にする必要があります。
--- DDLの場合 --- ALTER TABLE EMPL ALTER COLUMN PWD SET DATA TYPE VARCHAR ( 80) FOR BIT DATA NOT NULL --- DDSの場合 --- A PWD 80H COLHDG(' パスワード ')
フィールドのLengthについては、マニュアルに以下の様な記載がありました。
パスワードストリングが指定されているが、ヒント・ストリングは 指定されていない場合は、データ・ストリングの長さ属性に16 を足し、 それに次の8バイト境界までのバイト数を足したものになります。 それ以外の場合は、データ・ストリングの長さ属性に48を足し、それに次の8 バイト境界までのバイト数を足したものになります。
だそうです。
次に暗号化から復号化までの手順です。
暗号化パスワードは、暗号化する時と復号化する時に必要となります。ヒントはパスワードを忘れた時のヒントになります。ヒントは「select gethint(pwd) from テーブル 」の様にして、見ることが出来ます。
気を付けるのは、暗号化する際に書き込むフィールドは、無変換(CCSID65535)なので、書き込むジョブのCCSIDに左右されると言う事です。パスワード認証を行う際に、”入力パスワードを暗号化したモノ”とDBの暗号化されたパスワードを比較すると、現行のCCSIDと暗号化した時のCCSIDが違うと、当然不一致になります。しかし、”decrypt_char”を使うと、現行ジョブのCCSIDを反映した状態で復号化されるので、DBの暗号化パスワードを復号化した上で、入力パスワードと比較した方が、ジョブのCCSIDに影響される事がなさそうです。
■暗号化されたパスワード
認証処理の実装
■社員のパスワードを認証するクラス
<?php class Employee { const AUTH_SUCCESS = 1; const AUTH_NOTFOUND_USER= 2; const AUTH_INACTIVE_USER= 3; const AUTH_UNMATCH_PASS = 4; const AUTH_ERROR = 9; /** * 認証をする */ public function checkAuth($code,$password){ $db = ←DB2接続処理 // 認証 $sql="select" ." code,rtrim(name) as name ,del" ." ,(case when decrypt_char(pwd)=? then 1 else 0 end) as auth_flag" ." from empl" ." where code = ?"; $result = db2_prepare($db,$sql); db2_bind_param($result ,1 ,"password",DB2_PARAM_IN); db2_bind_param($result ,2 ,"code",DB2_PARAM_IN); if(!db2_execute($result)){ $flag = self::AUTH_ERROR; //エラー return $flag; } $row = db2_fetch_assoc($result); //認証判定 if ($row) { if ($row["del"] == "D"){ $flag = self::AUTH_INACTIVE_USER; //廃止ユーザ }elseif ($row["auth_flag"] == 0){ $flag = self::AUTH_UNMATCH_PASS; //パスワード不一致 }else{ $flag = self::AUTH_SUCCESS; //成功 } }else{ $flag = self::AUTH_NOTFOUND_USER; //ユーザーなし } return $flag; } } ?>
Zend_Auth_Adapterの実装
”Zend_Auth_Adapter”では”authenticate”メソッドを実装して、”Zend_Auth_Result”のインスタンスを返して、例外に”Zend_Auth_Exception”をスローする必要があります。前述のクラスのチェックメソッド(checkAuth)を使って、こんな感じで実装します。
<?php require_once 'Employee.php'; require_once 'Zend/Auth/Adapter/Interface.php'; require_once 'Zend/Auth/Adapter/Exception.php'; require_once 'Zend/Auth/Result.php'; class EmployeeAuthAdapter implements Zend_Auth_Adapter_Interface { private $id; private $password; private $name; public function __construct($id, $password){ $this->id = $id; $this->password = $password; } /** * 認証 */ public function authenticate() { $id = $this->id; $password = $this->password; $code = null; $message = ""; try { //社員インスタンス $empl = new Employee(); $ret = $empl->checkAuth($id,$password); $msgCode = $ret; //結果 switch ($msgCode) { case Employee::AUTH_SUCCESS: $code = Zend_Auth_Result::SUCCESS; $message = "認証OK"; break; case Employee::AUTH_NOTFOUND_USER: $code = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND; $message = "ユーザーが見つかりません"; break; case Employee::AUTH_INACTIVE_USER: $code = Zend_Auth_Result::FAILURE_UNCATEGORIZED; $message = "ユーザーが廃止されてます"; break; case Employee::AUTH_UNMATCH_PASS: $code = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID; $message = "パスワードが違います"; break; default: $code = Zend_Auth_Result::FAILURE_UNCATEGORIZED; $message = "その他のエラー"; break; } } catch (Exception $e) { throw new Zend_Auth_Exception("予期せぬ例外で認証エラー"); } $result = new Zend_Auth_Result($code,$id,array($message)); return $result; } } ?>
AuthAdapterの使用
実際に使用する時は、先ほど実装した”authenticate”メソッドで、Zend_Auth_Resultを取得して、”isValid”メソッドで認証します。trueが返れば成功です。認証結果のメッセージは”getMessages”で、メッセージのリストが返されます。
<?php require_once 'EmployeeAuthAdapter.php'; //認証アダプタ $adapter = new EmployeeAuthAdapter($id,$password,$autologin); //認証 $ret = $adapter->authenticate(); if($ret->isValid()){ //成功の処理 }else{ //失敗の処理 $message = $ret->getMessages(); echo $message[0] ; } ?>