symfony askeet Day 6続き
- ユーザー認証
- アクセスを制限する
- リファクタリング
ユーザー認証
まずはバリデータの設定をlogin.ymlに書く。
場所はaskeet/apps/frontend/modules/user/validate/login.yml
設定は完了したけど、テストデータにもデータモデルにもパスワードが存在しないので実装します。
まずはschema.ymlに実装。
ask_user: _attributes: { phpName: User, idMethod: native } id: { type: integer, required: true, primaryKey: true, autoIncrement: true } nickname: { type: varchar(50), required: true, index: true } first_name: varchar(100) last_name: varchar(100) email: varchar(100) sha1_password: varchar(40) salt: varchar(32) created_at: ~
書き終わったらsymfony propel-build-modelをして
symfony propel-build-sqlでSQL文を発行して
symfony propel-insert-sqlでMySQLにSQL文を流します。
パスワードの保存を行う処理をaskeet/lib/model/User.phpに実装。
public function setPassword($password) { $salt = md5(rand(100000, 999999).$this->getNickname().$this->getEmail()); $this->setSalt($salt); $this->setSha1Password(sha1($salt.$password)); }
$saltにはランダムな数とnicknameとemailをくっつけたものをMD5で暗号化したもの
それをパスワードとくっつけてSHA1で暗号化したものがsha1_passwordらしい。
sha1って何だろうと思ったので検索する。
調べてみるとMD4を元にした暗号化技術らしいのですが
どうも攻撃に成功してしまったらしい。つまり、復号可能ってことらしい。
だから、$saltを作ってパスワードが分かりづらくしていると。
参考:パスワードの保存に SMD5 (Salted MD5) や SSHA1を使う (MD5 への辞書攻撃とか) - まちゅダイアリー(2007-10-23)
参考:デファクトスタンダード暗号技術の大移行(1):すべてはここから始まった〜SHA-1の脆弱化 (1/2) - @IT
次にテストデータにパスワードとメールアドレスを追加する。
askeet/data/fixtures/test_data.php
User: anonymous: nickname: anonymous first_name: Anonymous last_name: Coward fabien: nickname: fabpot first_name: Fabien last_name: Potencier password: symfony email: fp@example.com francois: nickname: francoisz first_name: François last_name: Zaninotto password: adventical email: fz@example.com shota: nickname: crazyup first_name: Shota last_name: Enomoto password: shota email: crazyup@example.com
こんな感じで追加したら
php batch/load_data.php
で、反映。
アクセスを制限する
askeetは新規質問、関心を表す、コメントを評価するときなどにログインを求められるが、
それ以外はログインしなくても見られます。
ということでQuestionのaddアクションのアクセスを制限するために
askeet/apps/frontend/modules/question/config/security.ymlを作成する。
add: is_secure: on credentials: subscriber all: is_secure: off
addアクションにアクセスするには証明が必要だよと。
証明はsubscriberだよと。
それ以外は制限は掛けてない。こういう内容ですね。
ふむふむ。
内部構造の整理(リファクタリング)
ログインとログアウトをmyUser.class.phpにまとめる。
public function signIn($user) { $this->setAttribute('subscriber_id',$user->getId(),'subscriber'); $this->setAuthenticated(true); $this->addCredential('subscriber'); $this->setAttribute('nickname',$user->getNickname(),'subscriber'); } public function signOut() { $this->getAttributeHolder()->removeNamespace('subscriber'); $this->setAuthenticated(false); $this->clearCredentials(); }
ほげーっとまとめたらmyLoginValidator.class.phpの↓のように修正。
// password is OK? (パスワードは大丈夫?) if (sha1($user->getSalt().$password) == $user->getSha1Password()) { $this->getContext()->getUser()->signIn($user); return true; }
askeet/apps/frontend/modules/user/actions/actions.class.phpに
書かれているログアウト部分も修正。
public function executeLogout() { $this->getUser()->signOut(); $this->redirect('@homepage'); }
ほげっと終わらせたら、myUser.class.phpに
getSubscriverIdとgetSubscriberとgetNicknameのメソッドを書く。
public function getSubscriberId() { return $this->getAttribute('subscriber_id','','subscriber'); } public function getSubscriber() { return UserPeer::retrieveByPk($this->getSubscriberId()); } public function getNickname() { return $this->getAttribute('nickname','','subscriber'); }
getNicknameはlayout.phpで使えるみたいなので使う。
<div id="header"> <ul> <?php if ($sf_user->isAuthenticated()): ?> <li><?php echo link_to('sign out', 'user/logout') ?></li> <li><?php echo link_to($sf_user->getNickname().' profile', 'user/profile') ?></li> <?php else: ?> <li><?php echo link_to('sign in/register', 'user/login') ?></li> <?php endif ?> <li><?php echo link_to('about', '@homepage') ?></li> </ul> <h1><?php echo link_to(image_tag('askeet_logo.gif', 'alt=askeet'), '@homepage') ?></h1> </div>