Limit login attempts by IP address

This commit is contained in:
printempw 2018-07-25 15:28:59 +08:00
parent 8e43b185fe
commit 534224c212
4 changed files with 70 additions and 15 deletions

View File

@ -6,6 +6,7 @@ use Log;
use Mail;
use View;
use Utils;
use Cache;
use Cookie;
use Option;
use Session;
@ -41,7 +42,11 @@ class AuthController extends Controller
// it will return a null value.
$user = $users->get($identification, $authType);
if (session('login_fails', 0) > 3) {
// Require CAPTCHA if user fails to login more than 3 times
$loginFailsCacheKey = sha1('login_fails_'.Utils::getClientIp());
$loginFails = (int) Cache::get($loginFailsCacheKey, 0);
if ($loginFails > 3) {
if (strtolower($request->input('captcha')) != strtolower(session('phrase')))
return json(trans('auth.validation.captcha'), 1);
}
@ -50,7 +55,7 @@ class AuthController extends Controller
return json(trans('auth.validation.user'), 2);
} else {
if ($user->verifyPassword($request->input('password'))) {
Session::forget('login_fails');
Cache::forget($loginFailsCacheKey);
Session::put('uid' , $user->uid);
Session::put('token', $user->getToken());
@ -68,10 +73,11 @@ class AuthController extends Controller
->withCookie('uid', $user->uid, $time)
->withCookie('token', $user->getToken(), $time);
} else {
Session::put('login_fails', session('login_fails', 0) + 1);
// Increase the counter
Cache::put($loginFailsCacheKey, ++$loginFails);
return json(trans('auth.validation.password'), 1, [
'login_fails' => session('login_fails')
'login_fails' => $loginFails
]);
}
}

View File

@ -26,7 +26,7 @@
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="row" id="captcha-form" style="{{ (session('login_fails') > 3) ? '' : 'display: none;' }}">
<div class="row" id="captcha-form" style="{{ (Cache::get(sha1('login_fails_'.Utils::getClientIp())) > 3) ? '' : 'display: none;' }}">
<div class="col-xs-8">
<div class="form-group has-feedback">
<input id="captcha" type="text" class="form-control" placeholder="{{ trans('auth.captcha') }}">

View File

@ -76,6 +76,8 @@ class AuthControllerTest extends TestCase
$this->flushSession();
$loginFailsCacheKey = sha1('login_fails_'.Utils::getClientIp());
// Logging in should be failed if password is wrong
$this->post(
'/auth/login', [
@ -87,17 +89,17 @@ class AuthControllerTest extends TestCase
'msg' => trans('auth.validation.password'),
'login_fails' => 1
]
)->assertSessionHas('login_fails', 1);
); // Unable to assert cache content since array driver has unexpected behaviors
$this->flushCache();
$this->flushSession();
// Should check captcha if there are too many fails
$this->withSession(
[
'login_fails' => 4,
'phrase' => 'a'
]
)->post(
$this->withCache([
$loginFailsCacheKey => 4
])->withSession([
'phrase' => 'a'
])->post(
'/auth/login', [
'identification' => $user->email,
'password' => '12345678',
@ -107,6 +109,7 @@ class AuthControllerTest extends TestCase
'msg' => trans('auth.validation.captcha')
]);
$this->flushCache();
$this->flushSession();
// Should return a warning if user isn't existed
@ -121,8 +124,10 @@ class AuthControllerTest extends TestCase
$this->flushSession();
// Should clean the `login_fails` session if logged in successfully
$this->withSession(['login_fails' => 1])->post('/auth/login', [
// Should reset the `login_fails` counter if logged in successfully
$this->withCache([
$loginFailsCacheKey => 1
])->post('/auth/login', [
'identification' => $user->email,
'password' => '12345678'
])->seeJson(
@ -131,8 +136,9 @@ class AuthControllerTest extends TestCase
'msg' => trans('auth.login.success'),
'token' => $user->getToken()
]
)->assertSessionMissing('login_fails');
)->assertCacheMissing($loginFailsCacheKey);
$this->flushCache();
$this->flushSession();
// Logged in should be in success if logged in with player name

View File

@ -43,6 +43,49 @@ class TestCase extends Illuminate\Foundation\Testing\TestCase
return $this->withSession(['uid' => $role->uid, 'token' => $role->getToken()]);
}
/**
* Set the cache to the given array.
*
* @param array $data
* @param int $minutes
* @return $this
*/
public function withCache(array $data, $minutes = 60)
{
foreach ($data as $key => $value) {
$this->app['cache.store']->put($key, $value, $minutes);
}
return $this;
}
/**
* Assert that the cache does not have a given key.
*
* @param string|array $key
* @return void
*/
public function assertCacheMissing($key)
{
if (is_array($key)) {
foreach ($key as $k) {
$this->assertCacheMissing($k);
}
} else {
PHPUnit_Framework_Assert::assertFalse($this->app['cache.store']->has($key), "Cache has unexpected key: $key");
}
}
/**
* Flush all of the current cache data.
*
* @return void
*/
public function flushCache()
{
$this->app['cache.store']->flush();
}
protected function tearDown()
{
$this->beforeApplicationDestroyed(function () {