FuelPHPをやってみる (23) - 認証機能 (3) 独自のAuthドライバを実装する -

2012-12-01T00:00:00+00:00 FuelPHP PHP

前回でSimpleAuthを使って認証する方法がありましたが、これを独自実装でやりたい場合にAuthパッケージな機能を利用し認証ドライバを開発する事で組み込める模様。なのでちょっとやってみた

※一応、暫定版です。色々追記するかも

データベーステーブル仕様

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(80) NOT NULL,
  `password` varchar(50) NOT NULL,
  `email` varchar(255) NOT NULL,
  `salt` varchar(255) NOT NULL,
  `permission` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `userid` (`username`),
  UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

というようなテーブルを作る。で前回はやらなかったけど、指定したページのアクセス権限となる機能(Authパッケージで言うとGroupとかAclとか)を組み込む為にpermissionカラムに権限的なのを付ける

でまずはAuthドライバーパッケージは後回しで機能そういう機能的な所をコントローラー等に実装する

fuel/app/classess/controller/home.php

<?php

class Controller_Home extends Controller {

    public function before() {
        $auth = Auth::instance();

        // ログインしているかチェック
        if (!$auth->check()) {
            return Response::redirect("/auth");
        }

        // ログインユーザーのアクセス権限をチェック。この場合はpermissionカラムの値がadminであるかをチェック。そうじゃなければ/auth/deniedにリダイレクト
        if (!$auth->has_access("admin")) {
            return Response::redirect("/auth/denied");
        }
    }

    public function get_index() {
        // ログアウトのリンクだけを付けたビューなので詳細は省略
        return View::forge("home");
    }
}

まぁ前回同様ですが、今回はアクセス権限があるかまでチェックしたいのでそこら辺を使う。has_accessでログインユーザーが持つ権限でアクセス可能かをチェックする

でログインフォームだとかログイン処理を行うauthコントローラーを作る

fuel/app/classess/controller/auth.php

<?php

class Controller_Auth extends Controller {

    public function get_index() {
        // ログインフォームを出すだけ
        return View::forge("login");
    }

    public function post_login() {
        // ログインしてリダイレクト。ログインに失敗したかをチェックする場合にはloginメソッドの返り値等で実装を行う
        Auth::login();
        Response::redirect("/");
    }

    public function get_logout() {
        // ログアウトしてリダイレクト
        Auth::logout();
        Response::redirect("/");
    }

    // ログインユーザー権限でアクセス出来ない場合に表示されるアクション
    public function get_denied() {
        return Response::forge("Access Denied");
    }
}

これも前回のSimpleAuthとほとんど同じ。ビューはそのまま再利用しているので省略

でここから独自のAuthドライバーを作って、ログイン処理等を実装する

Authドライバの構成

fuel/app/classes/authに置く。で必要なのがLogin・Group・Aclなクラスの3つが必要になる。で今回はLiteっていうドライバ名にしたので

auth
├── acl
│   └── lite.php
├── group
│   └── lite.php
└── login
└── lite.php

っていうのが必要になるような感じで。んで認証ドライバを設定しなきゃならんのでfuel/app/config/auth.phpを

<?php

return array(
    "driver" => "Lite",
    "verify_multiple_logins" => false,
    "salt" => "put_your_salt_here",
);

な感じで以降で作るAuthドライバを指定しておく

fuel/app/classes/auth/login/lite.php

<?php

class Auth_Login_Lite extends Auth\Auth_Login_Driver {

    // Groupドライバの設定
    protected $config = array(
        "drivers" => array(
            "group" => array("Lite")
        )
    );

    private $user;

    protected function perform_check() {
        $current_user = Session::get("current_user");

        if (!is_null($current_user) && is_array($current_user)) {
            if (isset($current_user["id"]) && isset($current_user["salt"])) {
                $users = Model_User::find(array(
                    "select" => array("id", "username", "email", "permission"),
                    "where" => array(
                        "id" => $current_user["id"],
                        "salt" => $current_user["salt"]
                    ),
                    "limit" => 1
                ));

                if (!is_null($users) && is_array($users) && count($users) === 1) {
                    $this->user = reset($users);

                    return true;
                }
            }
        }

        return false;
    }

    public function validate_user($username = "", $password = "") {
        if (empty($username)) {
            $username = Input::post("userid");
        }

        if (empty($password)) {
            $password = Input::post("password");
        }

        $users = Model_User::find(array(
            "select" => array("id", "salt"),
            "where" => array(
                "username" => $username,
                "password" => sha1($password)
            ),
            "limit" => 1
        ));

        if (!is_null($users) && is_array($users) && count($users) === 1) {
            $user = reset($users);
            $user->salt = Str::random("sha2", 64);
            $user->save();

            Session::set("current_user", array(
                "id" => $user->id,
                "salt" => $user->salt
            ));

            $this->user = $user;

            return true;
        }

        return false;
    }

    public function login($username = "", $password = "") {
        return $this->validate_user($username, $password);
    }

    public function logout() {
        Session::delete("current_user");

        return true;
    }

    public function get_user_id() {
        if (!empty($this->user) && isset($this->user["id"])) {
            return array($this->id, (int)$this->user["id"]);
        }

        return null;
    }

    public function get_groups() {
        if (!empty($this->user) && isset($this->user["permission"])) {

            return array(
                array("Lite", $this->user["permission"])
            );
        }

        return false;
    }

    public function get_email() {
        if (!empty($this->user) && isset($this->user["email"])) {
            return $this->user["email"];
        }

        return null;
    }

    public function get_screen_name() {
        if (!empty($this->user) && isset($this->user["username"])) {
            return $this->user["username"];
        }

        return null;
    }

    public function has_access($condition, $driver = null, $entity = null) {
        if (is_null($entity) && !empty($this->user)) {
            $groups = $this->get_groups();
            $entity = reset($groups);
        }

        return parent::has_access($condition, $driver, $entity);
    }
}

コメント書かなくても何してるかは大体は分かると思うのですが、でどうやらhas_accessでもAuth::checkか何やらが走る模様なので、このクラスのperform_checkが数回走るので既にログインチェック済みかを検証して行った方が良いかと(そうしないとDBアクセスが数回走るという微妙な事になる)

んでhas_accessでアクセス権限チェックするのはGroupとAclの方になるのでそこを実装する

fuel/app/classes/auth/group/lite.php

<?php

class Auth_Group_Lite extends Auth\Auth_Group_Driver {

    // Aclの設定
    protected $config = array(
        "drivers" => array(
            "acl" => array("Lite")
        )
    );

    public function get_name($group = null) {
    }

    public function member($group, $user = null) {
        $auth = empty($user) ? Auth::instance() : Auth::instance($user[0]);
        $groups = $auth->get_groups();

        return in_array(array($this->id, $group), $groups);
    }
}

で実際に権限チェックを行う側はAclになる(Authのmemberメソッドでも呼び出せる)。memberメソッドはAclから呼び出すので

fuel/app/classes/auth/acl/lite.php

<?php

class Auth_Acl_Lite extends Auth\Auth_Acl_Driver {
    public function has_access($condition, Array $entity) {
        if (count($entity) > 0) {
            $group = Auth::group($entity[0]);

            if (!is_null($group)) {
                return $group->member($condition);
            }
        }

        return false;
    }
}

ってな感じな模様

で参考としてはドキュメントもぶっちゃけさっぱりな感じなので、SimpleAuthの実装を参考にした方が良いかと

Chrome Extension開発を勉強してみる (21) - chrome.experimental.commands - の補足 FuelPHPをやってみる (22) - 認証機能 (2) -