思路
1.需要一个表(user_login_info)负责记录用户登录的信息,不管登录成功还是失败都记录。并且登陆失败还是成功需要能够区分开来。
2.每次登陆时,都先从user_login_info表查询最近30分钟内(这里假设密码错误次数达到5次后,禁用用户30分钟)有没有相关密码错误的记录,然后统计一下记录总条数是否达到设定的错误次数。
3.如果在相同IP下,同一个用户,在30分钟内密码错误次数达到设定的错误次数,就不让用户登录了。
具体代码与及表设计
user_login_info表
CREATE TABLE `user_login_info` (
`id` int(10) UNSIGNED PRIMARY KEY AUTO_INCREMENT NOT NULL,
`uid` int(10) UNSIGNED NOT NULL,
`ipaddr` int(10) UNSIGNED NOT NULL COMMENT '用户登陆IP',
`logintime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
COMMENT '用户登陆时间',
`pass_wrong_time_status` tinyint(10) UNSIGNED NOT NULL COMMENT '登陆密码错误状态'
COMMENT '0 正确 2错误'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/**
* 检查用户最近$min分钟密码错误次数
* $uid 用户ID
* $lock_time 锁定时间
* $wTIme 错误次数
* @return 错误次数超过返回false,其他返回错误次数,提示用户
*/
public function checkPassWrongTime($uid, $lock_time=30, $wTime=3)
{
try{
$time = time();
$prevTime = time() - $lock_time*60;
//用户所在登录ip
$ip = ip2long( $_SERVER['REMOTE_ADDR'] );
$loginErrorCounts = UserLoginInfo::where(['uid'=>$uid,'pass_wrong_time_status'=>2,'ipaddr'=>$ip])
->where('UNIX_TIMESTAMP(logintime)','between',$prevTime,$time)
->count();
if($loginErrorCounts >= $wTime){
throw new Exception(100075);
}
}catch (Exception $exception){
throw new Exception($exception->getMessage());
}
}
//记录密码输出信息 type 2登陆错误,0正常
public static function recordPassWrongTime($uid,$type)
{
//ip2long()函数可以将IP地址转换成数字
$ip = ip2long($_SERVER['REMOTE_ADDR']);
$data['ipaddr'] = $ip;
$data['uid'] = $uid;
$data['pass_wrong_time_status'] = $type;
$data['logintime'] = date("Y-m-d H:i:s");
UserLoginInfo::create($data);
}
优化版:限制ip多次登陆
/**
* 检查用户最近$min分钟密码错误次数
* $uid 用户ID
* $lock_time 锁定时间
* $wTIme 错误次数
* @return 错误次数超过返回false,其他返回错误次数,提示用户
*/
public function checkPassWrongTime($uid, $lock_time=30, $wTime=5)
{
try{
$time = time();
$prevTime = time() - $lock_time*60;
//用户所在登录ip
$ip = $_SERVER['REMOTE_ADDR'];
$loginErrorCountsByIp = Db::name('user_login_info')->where(['pass_wrong_time_status'=>2,'ipaddr'=>$ip])
->where('logintime','between',[$prevTime,$time])
->count();
if($loginErrorCountsByIp >= $wTime){
throw new Exception('访问受限制');
}
$loginErrorCounts = Db::name('user_login_info')->where(['uid'=>$uid,'pass_wrong_time_status'=>2,'ipaddr'=>$ip])
->where('logintime','between',[$prevTime,$time])
->count();
if($loginErrorCounts >= $wTime){
throw new Exception('登录错误次数超出限制,请过三十分钟重新登录');
}
}catch (Exception $exception){
$this->error($exception->getMessage());
}
}
//记录密码输出信息 type 2登陆错误,0正常
public function recordPassWrongTime($uid,$type)
{
//ip2long()函数可以将IP地址转换成数字
$ip =$_SERVER['REMOTE_ADDR'];
$data['ipaddr'] = $ip;
$data['uid'] = $uid;
$data['pass_wrong_time_status'] = $type;
$data['logintime'] = time();
Db::name('user_login_info')->insert($data);
}