ユーザ認証とセッション管理のサンプル

Catalyst 5.65 で、下記のプラグインを使って実装してます。

MySqlMemcached は、事前にインストール済みであり、動作している事とします。

Catalyst::Plugin::ConfigLoader を利用しているので、まずは YAML 形式の設定ファイル。

---
name: CatalystMyTest

templates:
  bookmark:
    default: templates/bookmark/default.tt
    login:   templates/bookmark/login/default.tt

validator:
  plugins:
    - CreditCard
    - Japanese
  options:
    charset: euc
  messages: messages.yml

authentication:
  dbic:
    user_class: CatalystMyTest::Model::DBIC::Member
    user_field: name
    password_field: pass
  use_session: 1

session:
  expires: 3600
  memcached_new_args:
    data:
      - localhost:11211

templates は、ボクのオリジナル。今後、パスを解析して自動でテンプレートを選択するように end を書きかえたいと思ってます。
(Oct.12.2006 訂正: DefaultEnd 使った方が良い。また、View::TT を調べた所、template が空の場合は、action 名から template 名を自動生成してくれていた。)
validator は、今回 messages しか使っていないが、今後使う予定があるので一応追加。
他は、各プラグインのマニュアルとソースを確認の事。
authentication の use_session に条件式で真となる値を入れておくと、勝手にセッション管理してくれて楽チン。

validator の messages で指定されている messages.yml

---
login:
  name:
    NOT_BLANK: name を入力して下さい
    ASCII: name は ascii コードで入力して下さい
    LENGTH: name は 1 〜 10 バイトで入力して下さい
  pass:
    NOT_BLANK: pass を入力して下さい
    ASCII: pass は ascii コードで入力して下さい
    LENGTH: pass は 4 〜 10 バイトで入力して下さい
  name_pass:
    LOGIN: name か pass に誤りがあるので入力しなおして下さい。

次に Catalyst を use している所。

package CatalystMyTest;

use strict;
use warnings;

use Catalyst qw(
    -Debug
    ConfigLoader
    Static::Simple

    FormValidator::Simple
    FillInForm

    Authentication
    Authentication::Credential::Password
    Authentication::Store::DBIC

    Session
    Session::Store::Memcached
    Session::State::Cookie
);

our $VERSION = '0.01';

__PACKAGE__->setup;

sub default : Private {
    my ( $self, $c ) = @_;

    # Hello World
    $c->res->body( $c->welcome_message );
}

sub end : Private {
    my ( $self, $c ) = @_;
    $c->stash->{template}
	and !$c->res->body
	    and !$c->res->redirect
		and $c->forward( $c->view('TT') );
}

1;

Static::Simple は使用していないが、今後使う予定があるので一応追加。
default は、この path 配下でいろいろ試験予定なので、Hello World のまま放置。
end は、body や redirect に何も設定されておらず、templateファイル名が指定されている時のみ、TT を利用するようにしている。(他の方は、どうやってるんだろ?)

で、ログインしたユーザのみ利用できる箇所。

package CatalystMyTest::Controller::Bookmark;

use strict;
use warnings;
use base 'Catalyst::Controller';

sub auto : Private {
    my ( $self, $c ) = @_;
    $c->user_exists and return 1;
    $c->forward( '/bookmark/login/default' );
    return 0;
}

sub default : Private {
    my ( $self, $c ) = @_;
    $c->stash->{bookmark_rs} = $c->model( 'DBIC::Bookmark' )->search;
    $c->stash->{template} = $c->config->{templates}->{bookmark}->{default};
}

sub logout : Local {
    my ( $self, $c ) = @_;
    $c->logout;
    $c->forward( '/bookmark/login/default' );
}

1;

auto でユーザ認証を行う。
ここで戻り値として偽値を戻すと、end 以外のメソッドが実行されない。
user_exists で、ログインしているか否か判定可能。
蛇足ですが、ログインしていると

$c->res->body( 'ようこそ' . $c->user->name . 'さん' );

とかが可能です。(当然、name はテーブル内にフィールドがある事が前提)

上記で使用しているテンプレート。

<html>
<head><title>[% c.config.name %]</title></head>
<body>
<a href="/bookmark/logout">logout</a>
<hr>
[% WHILE (bookmark = bookmark_rs.next) %]
  <a href="[% bookmark.uri %]">[% bookmark.title %]</a><br>
[% END %]
</body>
</html>

で、最後にログインする箇所。

package CatalystMyTest::Controller::Bookmark::Login;

use strict;
use warnings;
use base 'Catalyst::Controller';

sub default : Private {
    my ( $self, $c ) = @_;

    $c->stash->{template} = $c->config->{templates}->{bookmark}->{login};
    $c->req->method eq 'GET' and return;

    $c->form(
      name => [qw/NOT_BLANK ASCII/, [qw/LENGTH 1 10/]],
      pass => [qw/NOT_BLANK ASCII/, [qw/LENGTH 4 10/]],
    );

    $c->form->has_error and return;

    $c->login( $c->req->param('name'), $c->req->param('pass') )
        or $c->set_invalid_form( 'name_pass' => 'LOGIN' );

    $c->form->has_error and return;

    $c->forward( '/bookmark/default' );
}

ログイン必要な path が /bookmark で、ログイン不要な path が /bookmark/login になってて、ちょっと変な感じですが、個人勉強用のやっつけなのでご勘弁を。
同じ path で GET と POST で動作を分けてます。
login を使うだけで、ユーザ認証からセッションへの登録まで、全て行ってくれます。

上記で使用しているテンプレート。

<html>
<head><title>[% c.config.name %]</title></head>
<body>
[% IF c.form.has_error %]
  Input Error
  <ul>
    [% FOREACH message IN c.form.messages('login') %]
      <li>[% message %]</li>
    [% END %]
  </ul>
[% END %]
<form action="/bookmark/login/" method="POST">
  <input type="text" name="name"><br>
  <input type="password" name="pass"><br>
  <input type="submit" value="login">
</form>
</body>
</html>


上記例、$c->forward の箇所を、全て $c->res->redirect に変更しても動作する事を確認してます。
ちょっと解り難い説明なので、時間がある時に清書します。