今回はそういったものを Yii で作ってみます。キャプチャは難しそうなので、まずは数回ログインを失敗すると、一定期間ログインできないような機能を追加してみます。
全体的な流れ
- login_attempt テーブルを作成
- LoginAttempt モデルを作成
- LoginController.php に試行回数の制限を実施するコードを追加
- login.php ビューの切り替え
1. login_attempt テーブルを作成
`id` int(11) NOT NULL AUTO_INCREMENT,`ip` varchar(32) NOT NULL,
`login` varchar(32) NOT NULL,
`create_time` int(11) NOT NULL,
`expiration_time` int(11) NOT NULL,
login カラムは、フォームから入力されたログイン情報が挿入されます。expiration_time は有効期限です。基本的に一定の短い期間に連続してログインに失敗したものを、IPアドレスの判定でログインできないようにする、という形をとります。
2. LoginAttempt モデルを作成
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class LoginAttempt extends ActiveRecord | |
{ | |
const LOGIN_ATTEMPT_LIMIT = 5; // ログイン試行回数のリミット | |
const BANNED_IP_EXPIRATION_TIME = '+1 hour'; // この場合は禁止されてから一時間経たないとログインできない | |
... | |
/** | |
* @see CActiveRecord::beforeSave() | |
*/ | |
protected function beforeSave() | |
{ | |
$this->create_time = time(); | |
$this->expiration_time = strtotime(self::BANNED_IP_EXPIRATION_TIME, $this->create_time); | |
return true; | |
} | |
/** | |
* ログインが禁止されたIPアドレスか、そうでないか | |
* @param string $ip ip address | |
* @return boolean | |
*/ | |
public function isBanned($ip) | |
{ | |
$c = $this->getCriteriaByIp($ip); | |
if ($this->count($c) >= self::LOGIN_ATTEMPT_LIMIT) { | |
return true; | |
} | |
return false; | |
} | |
/** | |
* ログインが禁止されたIPアドレスを期限切れの場合は削除する | |
* @param string $ip ip address | |
*/ | |
public function purgeBannedIp($ip) | |
{ | |
$c = $this->getCriteriaByIp($ip); | |
if ($this->count($c) >= self::LOGIN_ATTEMPT_LIMIT) { | |
$c->order = 't.create_time DESC'; | |
$model = $this->find($c); | |
if (time() > $model->expiration_time) { | |
$this->deleteAll('ip = :ip', array(':ip' => $ip)); | |
} | |
} | |
} | |
/** | |
* IPを条件にしたcriteriaを取得する | |
* @param string $ip ip address | |
* @return object criteria | |
*/ | |
private function getCriteriaByIp($ip) | |
{ | |
$c = new CDbCriteria; | |
$c->condition = 't.ip = :ip'; | |
$c->params[':ip'] = $ip; | |
return $c; | |
} | |
... |
3. LoginController.php に試行回数の制限を実施するコードを追加
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class LoginController extends Controller | |
{ | |
private $ip; | |
/** | |
* @see CController::defaultAction | |
*/ | |
public $defaultAction = 'login'; | |
/** | |
* @see CController::init() | |
*/ | |
public function init() | |
{ | |
$this->ip = Yii::app()->request->userHostAddress; | |
} | |
/** | |
* ユーザのログイン | |
*/ | |
public function actionLogin() | |
{ | |
if (Yii::app()->user->id) { | |
$this->redirect('/'); | |
} | |
$loginAttempt = new LoginAttempt(); | |
$loginAttempt->purgeBannedIp($this->ip); | |
$isBanned = $loginAttempt->isBanned($this->ip); | |
if (!$isBanned) { | |
$model = new Login(); | |
if (isset($_POST['Login'])) { | |
$model->attributes = $_POST['Login']; | |
if ($model->validate()) { | |
$this->redirect(Yii::app()->user->returnUrl); | |
} | |
if (!empty($model->login)) { | |
$loginAttempt->login = $model->login; | |
$loginAttempt->ip = $this->ip; | |
$loginAttempt->save(false); | |
} | |
} | |
} | |
$this->render('/user/login', compact('model', 'isBanned')); | |
} | |
} |
4. login.php ビューの切り替え
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php if (!$isBanned): ?> | |
<div class="form"> | |
<?php echo CHtml::form(); ?> | |
<?php echo CHtml::errorSummary($model, false); ?> | |
<div class="row"> | |
<?php echo CHtml::activeLabel($model, 'login'); ?> | |
<?php echo CHtml::activeTextField($model, 'login', array('maxlength' => 64)); ?> | |
</div><!-- /.row --> | |
<div class="row"> | |
<?php echo CHtml::activeLabel($model, 'password'); ?> | |
<?php echo CHtml::activePasswordField($model, 'password', array('maxlength' => 64)); ?> | |
</div><!-- /.row --> | |
<div class="row buttons"> | |
<?php echo CHtml::submitButton('ログイン'); ?> | |
<?php echo CHtml::activeCheckBox($model, 'rememberMe'); ?> | |
<?php echo CHtml::activeLabel($model, 'rememberMe'); ?> | |
</div><!-- /.row buttons --> | |
<?php echo CHtml::endForm(); ?> | |
</div><!-- /.form --> | |
<?php else: ?> | |
<div class="flash-error"> | |
ログイン試行回数が制限を越えてしまいました。時間をおいて、再度ログインを試してください | |
</div><!-- /.flash-error --> | |
<?php endif; ?> |
0 件のコメント:
コメントを投稿