laravel登录失败过多限制登录

简单使用

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出来修改即可。

  1. 修改最大错误登录次数,修改ThrottlesLogins的maxAttempts()方法
    public function maxAttempts()
    {
        return property_exists($this, 'maxAttempts') ? $this->maxAttempts : 5;
    }
  1. 修改限制登录时长,修改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)。

推荐阅读更多精彩内容