同福

做个用户管理系统(37)——通过安全问题重置密码【20201226】

介绍

介绍

福哥带着大家完成TFUMS的忘记密码功能的通过安全问题重置密码的后一部分功能的开发。这一部分的功能和通过绑定邮箱重置密码基本一样,福哥就不详细介绍了。

大家可以对照着看【20201224】的介绍了解设计思路,基本上都是一样的设计。通过安全问题重置密码功能开发完成之后,我们的忘记密码功能就算全部完成了。

随着忘记密码功能的竣工,我们的TFUMS系统的开发也要告一段落了。经过了37节课的学习,童鞋们了解到了一个项目从零到有的开发过程。从一个一个的功能模块的设计、开发、调试的过程中,童鞋们明白了哪些场合下应该如何设计,哪些场合下应该如何处理。从TFUMS的项目开发过程当中,我们学会了图像的处理应用(用户头像上传功能),学会了电子邮件的使用技巧(绑定邮箱功能、重置密码功能),学会了Redis的基本使用技巧(用户登录功能),还学会了AES算法的应用(忘记密码的激活码和验证码)。

经过这些之后,童鞋们应该就可以自己开发一些简单的项目了!福哥在此希望有兴趣的童鞋们可以做出一些功能强大、设计精致的作品出来!!!

模型user

resetPassBySecurityQuestions

public function resetPassBySecurityQuestions(string $dataEncrypted, string $newPwd):bool {
    $tfdo = $this->tfphp->getDatabase()->getTFDO();
    $tfredis = $this->tfphp->getDatabase()->getRedis();
    $myAES = new TFAES($this->tfphp);

    $dataMD5Key = md5($dataEncrypted). "resetPassBySecurityQuestions";
    if($tfredis->get($dataMD5Key) == $dataEncrypted){

        return 1;
    }

    $timestamp = date("YmdH0000");
    $data = $myAES->decrypt($dataEncrypted, TFConfig::get("projectAESPK", "system"). $timestamp, "");
    $arr = unserialize($data);
    if(!is_array($arr)
        || $arr['action'] != "resetPassBySecurityQuestions"
        || $arr['id'] == 0){
        $timestamp = date("YmdH0000", strtotime("-1 hour"));
        $data = $myAES->decrypt($dataEncrypted, TFConfig::get("projectAESPK", "system"). $timestamp, "");
        $arr = unserialize($data);
        if(!is_array($arr)
            || $arr['action'] != "resetPassBySecurityQuestions"
            || $arr['id'] == 0){

            return 1;
        }
    }

    $userID = $arr['id'];

    $userInfo = $this->getByTable("user", array($userID));
    if($userInfo == null){

        return 2;
    }

    $ret = $tfdo->update("user", array(
        'passwd'=>md5($newPwd)
    ), null, "userID = @int", array(
        $userID
    ));
    if(!$ret){

        return 3;
    }

    $tfredis->set($dataMD5Key, $dataEncrypted);
    $tfredis->expire($dataMD5Key, 7200);

    return 0;
}

接口控制器

user_process

protected function user_process(){
    $req = $this->tfphp->getRequest();
    $post = $req->post;
    $user = new user($this->tfphp);
    $data = $req->get->sn;
    $npass = $post->get("npass");
    $cpass = $post->get("cpass");

    try{
        // request test
        if($npass == "" || $cpass == ""){

            return $this->tfphp->getResponse()->responseJSON_CM(200, 1001101, "错误请求");
        }
        if($npass != $cpass){

            return $this->tfphp->getResponse()->responseJSON_CM(200, 1001101, "两次输入的新密码不一样");
        }

        // forgot by email
        $ret = $user->resetPassBySecurityQuestions($data, $npass);
        switch ($ret){
            case 1:
                return $this->tfphp->getResponse()->responseJSON_CM(200, 1001102, "验证码无效");
                break;
            case 2:
                return $this->tfphp->getResponse()->responseJSON_CM(200, 1001103, "用户不存在");
                break;
            case 3:
                return $this->tfphp->getResponse()->responseJSON_CM(200, 1001104, "重置密码失败");
                break;
        }
    }
    catch(\TypeError $e){

        return $this->tfphp->getResponse()->responseJSON_CM(200, 1001101, "错误请求");
    }

    // output
    return $this->tfphp->getResponse()->responseJSON_CM(200, 0, "OK");
}

视图模板

HTML代码

<!-- bind Email form begin -->
<div class="row login-form">
    <div class="col-sm-12">
        <h3 class="text-center">重置密码</h3>
        <p>请输入您的新密码</p>
        <form>
            <div class="form-group">
                <label>新密码</label>
                <input class="form-control" type="password" name="npass" />
            </div>
            <div class="form-group">
                <label>新确认密码</label>
                <input class="form-control" type="password" name="cpass" />
            </div>
            <div class="form-group">
                <button class="btn btn-primary btn-sm form-control">重置密码</button>
            </div>
        </form>
    </div>
</div>
<!-- bind Email form end -->

JS代码

$('form').form({
    url: "<% $TFReq->server->BASE_URI %>api/member/resetPassBySecurityQuestions?sn=<% $TFReq->get->data %>",
    method: "post",
    validations: [
        {type:"empty", name:"npass", msg:"请填写新密码"},
        {type:"min", value:6, name:"npass", msg:"新密码最少6个字"},
        {type:"empty", name:"cpass", msg:"请填写新确认密码"}
    ],
    onSuccess: function (d) {
        if(d.errcode == 0){
            document.location = '<% $TFReq->server->BASE_URI %>resetPassBySecurityQuestionsOK.htm?sn=<% $TFReq->get->data %>';
        }
        else{
            $('form').tips({
                text:d.errmsg
            });
        }
    },
    onError: function (d) {
        $('form').tips({
            text:"服务器响应错误"
        });
    },
    onValidationError: function (form, name, msg) {
        $('form').tips({
            text:msg
        });
        $('form').find('[name="'+ name +'"]').focus();
    }
});

讲解

模型user

resetPassBySecurityQuestions

首先检验Redis里是否有包含验证码MD5哈希码的值,如果有则表示用户已经使用这个验证码重置过密码了,就要拒绝再次操作。

接着校验验证码是否有效,如果无效就要报错。

然后检验用户是否存在,不存在就报错。

再来就是修改登录密码为新密码了。

最后要将验证码的MD5哈希码的值记录到Redis里,设置有效期2个小时,因为两个小时后验证码也会过期,就不存在安全问题了。

接口控制器

user_process

这是一个标准表单的处理后台程序,使用resetPassBySecurityQuestions重置用户的密码。

视图模板

HTML代码

这是重置密码表单界面,区别于修改密码表单,不需要提供原始密码。

JS代码

这是一个标准表单的JS驱动程序。

效果

重置密码表单界面。

a864337cc7243013.jpg

重置完密码界面。

因为resetPassBySecurityQuestionsOK页面就是一个静态页面,福哥不在这里提供说明了,大家可以自己建立起来。

9cb3d99f31df38f2.jpg

总结

福哥今天带着童鞋们完成了TFUMS系统的通过安全问题重置密码的后一部分功能的开发,同时也为TFUMS系统的教程暂时画上一个句号。

大家可以好好的总结一下经验,这可都是实实在在地项目开发经验哦~~