سلام دوستان
چطوری میشه تعداد لاگینهای اشتباه یه کاربرو شمارش کرد تا در صورتی که مثلا 5 بار لاگین اشتباه انجام داد برای 15 دقیقه از انجام لاگین محروم بشه؟
متشکرم.
یه فیلد تو جدول user در نظر بگیر که حکم شمارنده رو برات داشته باشه اگه کوئری SELECT مربوط به لاگین جوابی نداشت به این فیلد یکی اضافه کن بعدشم چک کن تعدادش از حد مجازش بیشتر نشده باشه ، اگه هم مثلا" طرف 2 بار لاگین ناموفق داشت دفعه ی سوم login درستی داشت اون فیلد رو به حالت 0 برگردون ... سناریوهای مختلفی وجود داره ولی همشون تو قسمتی که کاربر login ناموفق داشته مشترک هستن
اگه کاربری که در جدول نام کاربری نداره بخواد لاگین کنه چی؟
بهتر نیست بوسیله گرفتن IP کاربر، شمارش رو انجام بدیم و براش سشن و کوکی بسازیم؟
تغییراتی که برای شمارش لاگینهای اشتباه و دادن پیام لازم بود رو به سیستم لاگین اضافه کردم.
اول یک جدول بصورت زیر میسازیم:
CREATE TABLE IF NOT EXISTS `attempts` (
`ip` varchar(20) NOT NULL ,
`when` int(11) NOT NULL ,
`del` int(11) DEFAULT NULL ,
KEY `ip` (`ip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
بعد تغییرات را در actionAdminLogin اعمال میکنیم:
public function actionAdminLogin()
{
$model=new LoginForm;
if(isset($_POST['LoginForm']))
{
$model->attributes=$_POST['LoginForm'];
if($model->validate() && $model->login()){
$this->redirect(array('default/index'));
}
else{
// اگه از قبل رکوردی برای این آی پی در جدول هست آن را حذف کن
Attempts::model()->deleteAll('ip=:Ip AND `when` <= :When AND del=1',array(':Ip'=> $_SERVER['REMOTE_ADDR'], ':When'=> time()));
// یک مدل جدید برای اشتباه فعلی ایجاد کن و آن را ذخیره کن
$insertAttempt= new Attempts;
$insertAttempt->ip= $_SERVER['REMOTE_ADDR'];
$insertAttempt->when= time()+1800;
$insertAttempt->del= 0;
$insertAttempt->save();
// اگر تعداد اشتباهات به سه شماره رسید
// رکوردهایی که برای این آی پی درج کرده ای را آماده حذف شدن قرار بده
// بعد به صفحه اصلی برو
$countFaild= (int)Attempts::model()->count('ip=:ip', array(':ip' => $_SERVER['REMOTE_ADDR']));
$maxAttempts=3;
if($countFaild>= $maxAttempts){
Attempts::model()->updateAll(array('del'=>1), 'ip=:Ip', array(':Ip'=> $_SERVER['REMOTE_ADDR']));
$this->redirect(array('default/index'));
}
}
}
و در آخر تغییراتی را در actionIndex اعمال میکنیم:
public function actionIndex()
{
// همه فیلدهای زمان را که این آی پی را دارند را انتخاب کن
$selectAttempts= Attempts::model()->findAll(
array(
'select'=>'`when`',
'condition'=>'ip=:Ip',
'params'=>array(
':Ip'=> $_SERVER['REMOTE_ADDR'],
)
)
);
if($selectAttempts){
$when=array();
foreach($selectAttempts as $key => $selectAttempt){
$when[]= $selectAttempt['when'];
}
// بیشترین مقدار زمانهای انتخاب شده را بگیر
$maxTime= max($when);
// اگر هنوز زمان فعلی از زمان دریافت شده از جدول کمتر است پیام خطا را نشان بده
if(time()<=$maxTime){
throw new CHttpException(404,'شما سه بار نام کاربری یا رمز عبور را اشتباه وارد کرده اید و برای نیم ساعت مجاز به دیدن صفحه لاگین نیستید.');
}
}
$this->render('index');
}
دوستان اگه میشه کدها رو بهتر از این نوشت لطفا تغییرات لازم رو اعمال کنید.
متشکرم.
1- منم امروز همینا رو نوشتم، فقط اون فیلد del چیه اضافه کردی؟ چرا؟
2- بعد انگار یک کم منطق برنامه رو عوض کردی، نه؟ کدها اینطوری بود که زمان حال منهای ثانیه دلخواه رو یک متغییری فرض کنه و رکوردهایی که قدیمی تر از این زمان متغییر بود رو پاک کنه و موقع insert زمان رو همون زمان حال میذاشت. ولی انگار تو اومدی رکوردهایی که زمانشون از زمان الان کمتره رو پاک میکنی، ولی موقع insert زمان حال بعلاوه ثانیه دلخواه رو توی فیلد when ثبت میکنی. درسته؟
3- راستی چرا وقتی تعداد دفعات لاگین از حد مجاز میگذره بعدش بازم کد $this->redirect(array('default/index')); رو نوشتی؟ این مگر برای موقع لاگین موفقیت آمیز نیست؟
1- فیلد del مال زمانیه که بعد از نیم ساعت، کاربر دوباره میتونه صفحه لاگین رو ببینه. حالا اگه بازم در وارد کردن پسورد اشتباه کنه، رکورد هایی که از دفه قبل هستن و فیلد del در اونها 1 شده، الآن حذف میشن تا برای این آی پی رکوردهای جدیدی ساخته بشه. اینجا مستقیما رکوردها رو حذف نکردم چون در اکشن index بهشون نیاز دارم.
2- در موقع insert نیم ساعت بعد از الآنو ذخیره میکنم. سه بار این درج رکورد انجام میشه که فیلد del در آونها صفر هست.
3- وقتی اشتباهات سه تا شد اول فیلد del در سه رکورد ذخیره شده یک میشه تا بعد نیم ساعت بتونم این رکوردا رو پاک کنم. بعد صفحه رو redirect میکنم به اکشن index تا کدهایی که در اکشن index اضافه کرده ام اجرا بشن و پیام خطا نشون داده بشه. در واقع تا نیم ساعت اکشن index چیزی جز پیام خطا نشون نمیده.
کدهای این تاپیک رو برا yii2 آپدیت کردم:
public function actionLogin()
{
if (!Yii::$app->admin->isGuest) {
return $this->redirect(['index']);
}
//در این قسمت چک میکنیم اگه در ورود کاربر قبلا خطا بوده صفحه ورود را نبیند
$checklogins = Checklogin::findAll(['ip'=>$_SERVER['REMOTE_ADDR'],'del'=>1]);
if($checklogins){
$when = [];
foreach($checklogins as $checklogin){
$when[]= intval($checklogin['when']);
}
if(time() <= max($when)){
throw new yiiwebHttpException(404,'شما سه بار نام کاربری یا رمز عبور را اشتباه وارد کرده اید و برای یک ساعت مجاز به دیدن صفحه ورود نیستید.');
}
}
// اگر خطایی نبوده صفحه ورود را ببیند
$this->view->title = 'ورود';
$this->view->params['breadcrumbs'][] = $this->view->title;
$model = new LoginForm();
if ($model->load(Yii::$app->request->post()) && $model->login()) {
// اگه کمتر از سه خطا در موقع ورود داشته بعد از ورود خطاها پاک میشه و به صفحه اصلی میره
$ip = $_SERVER['REMOTE_ADDR'];
Checklogin::deleteAll(['ip'=>$ip]);
return $this->redirect(['index']);
}
else{
// اگه خطایی در موقع ورود اتفاق افتاد به صورت زیر عمل بشه
$ip = $_SERVER['REMOTE_ADDR'];
Checklogin::deleteAll(['ip'=>$ip,'del'=>1]);
$checklogin = new Checklogin();
$checklogin->ip = $ip;
$checklogin->when = strval(time()+Yii::$app->params['check_login_time']);
$checklogin->del = 0;
$checklogin->save();
$faild = Checklogin::find()->where(['ip'=>$ip])->count();
if($faild == 3 ){
Yii::$app->session->setFlash('warning','لطفا دقت کنید. در صورت اشتباه وارد کردن تا یک ساعت این صفحه برایتان مسدود میشود.');
}
if($faild == 4 ){
Checklogin::updateAll(['del'=>1],'del=0');
throw new yiiwebHttpException(404,'شما سه بار نام کاربری یا رمز عبور را اشتباه وارد کرده اید و برای یک ساعت مجاز به دیدن صفحه ورود نیستید.');
}
}
return $this->render('login', compact('model'));