简单使用
laravel有自带的登录限制:ThrottlesLogins;位于Illuminate\Foundation\Auth\ThrottlesLogins
,是一个trait,use进来即可;这个trait实现了1分钟内登录5次失败就禁止登录1分钟.
登录代码:
public function postLogin(Request $request)
{
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
$credentials = $request->only([$this->username(), 'password']);
/** @var \Illuminate\Validation\Validator $validator */
$validator = Validator::make($credentials, [
$this->username() => 'required',
'password' => 'required',
]);
if ($validator->fails()) {
$this->incrementLoginAttempts($request);
return back()->withInput()->withErrors($validator);
}
if (! $this->guard()->attempt($credentials)) {
$this->incrementLoginAttempts($request);
return back()->withInput()->withErrors([
'password' => $this->getFailedLoginMessage(),
]);
}
return $this->sendLoginResponse($request);
}
它的实现很简单,代码并不复杂,两个缓存:
一个缓存记录它失败的次数;键为用户名和ip的组合:Str::lower($request->input($this->username())).'|'.$request->ip();
;值为登录失败次数;缓存有效期即为登录失败次数的限定时长,默认一分钟。
另一个缓存则记录它失败设定次数后后锁定时长; 键为上面的键拼接上:timer
;值为第一次登录失败时的时间加上限制登录时长;缓存有效期即为限制登录时长,默认一分钟
在登录时根据第一个缓存判断失败次数是否达到设定次数;若没有,走正常的登录流程,若是,则锁定禁止登录。登录失败时通过方法$this->incrementLoginAttempts($request);
这个方法调用如下:
public function hit($key, $decayMinutes = 1)
{
$this->cache->add(
$key.':timer', $this->availableAt($decayMinutes * 60), $decayMinutes
);
$added = $this->cache->add($key, 0, $decayMinutes);
$hits = (int) $this->cache->increment($key);
if (! $added && $hits == 1) {
$this->cache->put($key, 1, $decayMinutes);
}
return $hits;
}
如果是第一次登录失败就初始化两个缓存,否则就增加一次错误次数increment($key)
这个trait主要的还是防刷机器登录的,因为他在第一次登录失败就写入了第二个缓存(add 方法将只存储缓存中不存在的数据,不会覆写),这就导致了一个问题:假如我在这一分钟的第一秒失败了第一次,而在这一分钟的最后一秒失败了最后一次的话,那我下一分钟还是可以继续登录。个人认为应该是达到错误次数后才写入第二个缓存。
如果有其他限制需求怎么做呢
也很简单,复制一份ThrottlesLogins和RateLimiter出来修改即可。
- 修改最大错误登录次数,修改ThrottlesLogins的maxAttempts()方法
public function maxAttempts()
{
return property_exists($this, 'maxAttempts') ? $this->maxAttempts : 5;
}
- 修改限制登录时长,修改ThrottlesLogins的decayMinutes()方法; 单位:分钟
public function decayMinutes()
{
return property_exists($this, 'decayMinutes') ? $this->decayMinutes : 1;
}
需要注意的是,修改了限制登录时长的同时也修改了错误登录时间区间;因为上面提到的hit()方法在写入第一个缓存时使用的缓存有效期也是方法decayMinutes()的返回值。所以我们有需要就修改RateLimiter的hit()方法;
例子:20分钟内失败5次锁定30分钟,从最后一次登录失败开始算
public function hit($key, $decayMinutes = 1)
{
$added = $this->cache->add($key, 0, 20);
$hits = (int) $this->cache->increment($key);
if (! $added && $hits == 1) {
$this->cache->put($key, 1, 20);
}
if($hits == 5){
$this->cache->add(
$key.':timer', $this->availableAt($decayMinutes * 60), $decayMinutes
);
}
return $hits;
}
其中的细节也可以慢慢修改,尽善尽美。比如限制登录的提示信息等;若还有其他更复杂的限制需求,修改这几个方法就可达到要求,源代码也就这两个类(一个trait一个class)。