之前由于做一个小项目的需要,做一个用户登录密码输错几次之后被锁定。但是本人还觉得还有瑕疵,就是在连续输错几次之后被锁定,但是目前实现的是时间上不连续错误几次就被锁定!
MODEL类中的代码
class UserModel extends Model
{
protected $trueTableName ="mgj_administrators";
// 数据映射
protected $_map = array(
// 把表单中name映射到数据表的username字段
'username' =>'username',
'userpassword' =>'password',
// 把表单中的mail映射到数据表的email字段
);
//检查用户名是否存在
protected $_validate = array(
array('username','require','用户名必须'),
array('password','require','用户密码必须'),
// 都有时间都验证
array('username','checkName','帐号错误!',1,'callback'), // 只在登录时候验证
// array('password','checkPwd','密码错误!',1,'callback'),
// 只在登录时候验证
);
public function checkName()
{
$username = I('post.username');
$map['username'] = $username;
$res = $this->field('username')->where($map)->find();
if(!$res){
return false;
}
}
// 处理登录
public function signIn()
{
$data = I('post.');
$map['username'] = $data['username'];
$map['status'] = 1;
$userInfo = $this->where($map)->find();
if(!$userInfo){
return 1;//用户名不存在或者已经被禁用
}
//检查用户最近30分钟密码错误次数
$res = $this->checkPassWrongTime($userInfo['id']);
if(!$res){
return 30;//表示被禁用30分钟
}
$bool = password_verify($data['userpassword'], $userInfo['password']);
if(!$bool){
//记录密码错误次数
$this->recordPassWrongTime($userInfo['id']);
return 2;//密码不正确
}else{
// 登录成功,将用户信息保存在session中,不保存用户的密码
unset($userInfo['password']);
$_SESSION['mgjAdmin'] = $userInfo;
return 3;//登录成功
}
if( $data['online']==1 ){
$_SESSION['mgjAdmin']['username'] = $data['name'];
$_SESSION['mgjAdmin']['password'] = $data['userpassword'];
setcookie('username',$data['name'],time()+60*60*7*24);
setcookie('password',$data['userpassword'],time()+60*60*7*24);
}
}
//这个方法是用来检验用户近30 分钟错误的次数
public function checkPassWrongTime($uid, $min=30, $wTime =4)
{
if ( empty($uid) ){
throw new \Exception('第一个参数不能为空');
}
$time = time();//当前时间
$prevTime = time() - $min*60;
//用户登录ip
$ip = -ip2long( $_SERVER['REMOTE_ADDR'] ) ;
$map['uid'] = $uid;
$map['pass_wrong_time_status'] =2;//表示错误的
$map['ipaddr'] = $ip;
$map['longintime'] = array( 'between', $prevTime, $time );
$worngData = M('user_lock')->field('id,uid,ipaddr,logintime,pass_wrong_time_status')->where($map)->select();
//错误次数的统计
$worngTimes = count($worngData);
// 判断错误次数是否超过了限制次数
if( $worngTimes > $wTime ){
// 如果错误大于了还需要查询最后一次错误的时间
$map['uid'] = $uid;
$userLockTime = M('user_lock')->where($map)->order('logintime desc')->find();
if( $userLockTime['logintime'] -time() < 60*30 ){
// 将状态修改为1,正确状态
$data['pass_wrong_time_status'] = 1;
M('user_lock')->where("uid=".$uid)->save($data);
}
return false;
}else{
return true;
}
}
//这个方法是用来记录密码错误的次数的
protected function recordPassWrongTime($uid)
{
//ip2lon()函数可以将IP地址转化为数字
$ip = ip2long( $_SERVER['REMOTE_ADDR'] );
//时间为时间戳
$time = time();
$data['uid'] =$uid;
$data['ipaddr'] = $ip;
$data['logintime'] = $time;
//状态为2表示正常,没有被锁定
$data['pass_wrong_time_status'] = 2;
M('user_lock')->add($data);
}
}
Controller中代码
class PublicController extends Controller
{
public function login()
{
if ( IS_GET ) {
$this->display('Index/login');
}else if(IS_POST){
if (!D('User')->create()){
// 如果创建失败 表示验证没有通过 输出错误提示信息
exit(D('User')->getError());
}
//先判断验证码是否正确
$bool = $this->checkCode( I('post.code') );
if (!$bool ) {
$this->error('验证码错误');
}
$res = D('User')->signIn();
if ( $res==1 ) {
$this->error('用户名不存在或者已经被禁用');
exit;
}else if($res==3){
$this->assign('admin', $_SESSION['mgjAdmin']['username']);
$this->success('登录成功', U('Index/index'));
exit;
}else if($res==2){
$this->error('用户名或者密码错误');
exit;
}else if($res==30){
$this->error('亲,你已经被禁用30分钟,一会儿再来!');
exit;
}
}
}
public function makeCode()
{
$config = array(
'fontSize' =>25,
// 验证码字体大小
'length' => 4,
//验证码宽度、高度
'imageW' =>180,
'imageH' =>50,
// 验证码位数
);
$Verify = new \Think\Verify($config);
$Verify->entry();
}
public function checkCode($code)
{
// 将array(‘reset’=>false)错误了之后不清session中验证码以便于在此验证 array('reset'=>false)
$Verify = new \Think\Verify(array('reset'=>false) );
if($Verify->check($code)){
return true;
}else{
return false;
}
}
//这个方法负责ajax验证码的验证
public function checkAjaxVerify()
{
if(!IS_AJAX)$this->error('亲,别乱来!',1);
$codeVal = I('post.codeVal');
if( $this->checkCode($codeVal) ){
echo 1;
}else{
echo 0;
}
}
// 这个方法负责ajax验证
public function ajaxCheckUser()
{
if(!IS_AJAX){
exit('亲,别乱来!');
}else if(IS_AJAX){
$userval = I('post.userval');
// dump($userval);
$map['username'] = $userval;
$res = M('administrators')->where($map)->find();
if(!$res){
echo 1;//用户名不存在
}else{
echo 2;//有同样
}
}
}
// 这个方法负责退出
public function signOut()
{
// 先清除掉cookie,在清除掉session中的
if(isset($_COOKIE[session_name()]))setCookie(session_name(),'',time()-1,'/');
// 摧毁垃圾文件
session_destroy($_SESSION['mgjadmin']);
$this->display('Index/login');
}
}
数据库的设计
--用户锁定表mgj_User_Lock ipaddr位
create table mgj_User_Lock(
id int unsigned auto_increment primary key,
uid int unsigned not null,
ipaddr int not null,
logintime int not null default 0 P COMMENT '用户登陆时间',
pass_wrong_time_status tinyint(10) NOT NULL COMMENT '登陆密码错误状态1 启用 2禁用 '
)engine = innodb default charset= utf8;
alter table mgj_user_lock modify ipaddr int not null;
alter table mgj_user_lock modify logintime int not null default 0;
--commit"管理员表"status 为1启用,2禁用
create table mgj_Administrators(
id int unsigned auto_increment primary key,
username varchar(32) not null,
password varchar(100) not null,
status tinyint(1) unsigned default 1,
pic varchar(50) not null,
email varchar(100) not null,
addtime int not null
)engine=innodb default charset =utf8;
alter table mgj_Administrators add unique(`username`);
alter table mgj_Administrators modify password varchar(100) not null;