同福

做个用户管理系统(36)——使用安全问题重置密码【20201225】

介绍

介绍

福哥今天带着大家完成TFUMS系统的忘记密码功能的使用安全问题重置密码的前一部分功能的开发。使用安全问题重置密码相对比较简单了,我们只需要将用户设置过的安全问题的题目和答案取出来给用户答,如果用户能完全答对就表示用户是账号的所有者,我们就允许他来重置用户的登录密码了。

验证安全问题需要注意几个问题:

  • 必须将三个问题都调出来给用户来答,缺一不可

  • 载入问题题目的接口里不要将答案泄漏出去了

  • 判断答案的时候需要将三个问题和答案一一对应

模型user

forgotBySecurityQuestions

public function forgotBySecurityQuestions(int $userID, string $a1, string $a2, string $a3, string &$dataEncrypted){
    $myAES = new TFAES($this->tfphp);

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

        return 1;
    }

    $securityQuestions = $this->getSecurityQuestions($userID);
    if(!($securityQuestions['q1'] > 0
        && $securityQuestions['q2'] > 0
        && $securityQuestions['q3'] > 0)){

        return 2;
    }
    if($a1 != $securityQuestions['a1']
        || $a2 != $securityQuestions['a2']
        || $a3 != $securityQuestions['a3']){

        return 3;
    }

    $data = serialize(array('action'=>"resetPassBySecurityQuestions", 'id'=>$userID, 'timestamp'=>time()));
    $dataEncrypted = $myAES->encrypt($data, TFConfig::get("projectAESPK", "system"). date("YmdH0000"), "");

    return 0;
}

接口控制器

doList

private function doList(){
    $user = new user($this->tfphp);
    $data = $this->tfphp->getRequest()->get->sn;

    // check data
    $userInfo = $user->forgotWay($data);
    if($userInfo == null){

        return null;
    }

    // get states
    $securityStates = $user->getSecurityStates($userInfo['userID']);

    // make new
    $newArr = array();
    if($securityStates['securityQuestions']){
        $securityQuestions = $user->getSecurityQuestions($userInfo['userID']);
        $newArr = array(
            'q1'=>Dictionary::$securityQuestionArr[$securityQuestions['q1']],
            'q2'=>Dictionary::$securityQuestionArr[$securityQuestions['q2']],
            'q3'=>Dictionary::$securityQuestionArr[$securityQuestions['q3']],
        );
    }

    return $this->tfphp->getResponse()->responseJSON(200, $newArr);
}

doVerify

private function doVerify(){
    $req = $this->tfphp->getRequest();
    $post = $req->post;
    $user = new user($this->tfphp);
    $data = $req->get->sn;
    $a1 = $post->get("a1");
    $a2 = $post->get("a2");
    $a3 = $post->get("a3");

    $dataEncrypted = "";
    try{
        // check data
        $userInfo = $user->forgotWay($data);
        if($userInfo == null){

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

        // forgot by email
        $ret = $user->forgotBySecurityQuestions($userInfo['userID'], $a1, $a2, $a3, $dataEncrypted);
        switch ($ret){
            case 1:
                return $this->tfphp->getResponse()->responseJSON_CM(200, 1001112, "用户名不存在");
                break;
            case 2:
                return $this->tfphp->getResponse()->responseJSON_CM(200, 1001113, "安全问题无效");
                break;
            case 3:
                return $this->tfphp->getResponse()->responseJSON_CM(200, 1001114, "答案错误");
                break;
        }
    }
    catch(\TypeError $e){

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

    // output
    return $this->tfphp->getResponse()->responseJSON_CM(200, 0, "OK", array('data'=>$dataEncrypted));
}

视图模板

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>
                <span class="form-control" id="q1"></span>
            </div>
            <div class="form-group">
                <label>第一个问题的答案</label>
                <input class="form-control" type="text" name="a1" />
            </div>
            <div class="form-group">
                <label>第二个问题</label>
                <span class="form-control" id="q2"></span>
            </div>
            <div class="form-group">
                <label>第二个问题的答案</label>
                <input class="form-control" type="text" name="a2" />
            </div>
            <div class="form-group">
                <label>第三个问题</label>
                <span class="form-control" id="q3"></span>
            </div>
            <div class="form-group">
                <label>第三个问题的答案</label>
                <input class="form-control" type="text" name="a3" />
            </div>
            <div class="form-group">
                <button class="btn btn-primary btn-sm form-control">提交答案</button>
            </div>
        </form>
    </div>
</div>
<!-- bind Email form end -->

JS代码

$.get("<% $TFReq->server->BASE_URI %>api/member/forgotBySecurityQuestions/_list", {sn:"<% $TFReq->get->sn %>"}, function (d) {
    $('#q1').html(d.q1);
    $('#q2').html(d.q2);
    $('#q3').html(d.q3);
},"json");
// form
$('form').form({
    url: "<% $TFReq->server->BASE_URI %>api/member/forgotBySecurityQuestions/_verify?sn=<% $TFReq->get->sn %>",
    method: "post",
    validations: [

    ],
    onSuccess: function (d) {
        if(d.errcode == 0){
            document.location = '<% $TFReq->server->BASE_URI %>resetPassBySecurityQuestions.htm?data='+d.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

forgotBySecurityQuestions

首先判断用户名是否存在,不存在就报错。

接着判断是否设置了安全问题,没有设置就报错。

然后判断安全问题的答案对不对。

最后使用AES算法将重置密码数据进行加密获得一个验证码。

接口控制器

doList

获取安全问题的文字内容,数据库里存储的是安全问题的编号,需要通过编号到Dictionary里面兑换问题文字。

doVerify

使用模型user的forgotBySecurityQuestions方法验证安全问题的答案并获取验证码。

视图模板

HTML代码

这是一个标准表单界面代码,里面的安全问题文字放到了伪装成输入框的span里面了。

JS代码

这是一个标准表单JS驱动代码,处理成功后会转入重置密码页面。

效果

这是验证安全问题的界面。

8eea540cf4057ebe.jpg

总结

福哥今天带着童鞋们完成了TFUMS的使用安全问题重置密码的前一部分功能的开发。主要还是依赖AES算法将隐藏信息带给重置密码的表单作为依据,这一点和使用绑定邮箱重置密码的思路是一样的。区别在于不需要用户做额外的操作,如果安全问题的答案是正确的话,马上就可以重置密码了。

下一课,福哥将带着大家完成通过安全问题重置密码的后一部分功能的开发,敬请期待~~