介绍
介绍
福哥今天要带着大家完成绑定邮箱功能的前半部分——发送绑定邮箱激活码邮件。这里面有几件难题:1)要实现邮件激活码,需要设计一个可逆的加密/解密功能;2)激活链接必须是一个完整的URL地址;3)激活链接必须有时效性。
第一个问题,福哥经过再三考虑决定使用AES算法实现一个可逆的加密/解密功能,为了保证每次的激活码不一样,原数据增加随机串。
第二个问题,因为页面控制器和接口控制器不一定会配置在同一个目录下面(虽然大多数情况下是同一个),所以对外访问的URL前缀不能直接用接口控制器的作为页面控制器的。这里福哥在配置文件里面加了一个outUrl的配置参数,用这个参数就错不了。
第三个问题,福哥在激活码原数据里面增加了一个时间戳,精确到小时。这样在验证的时候,如果当前时间小时和激活码的一样才能正确解密,能正常解密就证明激活码是有效的。为了避免误差,除了测试当前时间还要测试一个小时前的时间。
模型user
sendBindEmail
public function sendBindEmail(int $userID, string $email):bool { $myMail = new TFMail($this->tfphp); $myAES = new TFAES($this->tfphp); $userInfo = $this->getByTable("user", array($userID)); if($userInfo == null){ return 1; } $userInfo = $this->getByTable("userByEmail", array($email)); if($userInfo != null && $userInfo['userID'] != $userID){ return 2; } $SMTPArgs = TFConfig::get("SMTPArgs", "system"); $subject = "激活你的TFUMS系统账号的绑定邮箱"; $linkData = serialize(array('id'=>$userID, 'email'=>$email, 'timestamp'=>time())); $linkDataEncrypted = $myAES->encrypt($linkData, TFConfig::get("projectAESPK", "system"). date("YmdH0000"), ""); $link = TFConfig::get("outUrl", "system"). "member/bindEmailVerify.htm?data=". $linkDataEncrypted; $body = "你好!<br/> <br/> 请点击下面的链接完成邮箱绑定查资!<br/><br/> <a href=\"". $link. "\" target=\"_blank\">". $link. "</a><br/> <br/> TFUMS<br/> ". $SMTPArgs['email']. "<br/>"; $myMail->setSMTP($SMTPArgs['host'], $SMTPArgs['port'], $SMTPArgs['user'], $SMTPArgs['pwd']); if(!$myMail->send($subject, $body, array( 'from'=>$SMTPArgs['email'], 'to'=>$email, ))){ return 3; } return 0; }
接口控制器
doSend
private function doSend(){ $req = $this->tfphp->getRequest(); $post = $req->post; $user = new user($this->tfphp); $email = $post->get("email"); try{ // request test if($email == ""){ return $this->tfphp->getResponse()->responseJSON_CM(200, 1001061, "错误请求"); } // create user $ret = $user->sendBindEmail($this->permission->getLoginStatus()->userID, $email); switch ($ret){ case 1: return $this->tfphp->getResponse()->responseJSON_CM(200, 1001062, "用户名不存在"); break; case 2: return $this->tfphp->getResponse()->responseJSON_CM(200, 1001063, "邮箱地址已经存在"); break; case 3: return $this->tfphp->getResponse()->responseJSON_CM(200, 1001064, "发送邮件失败"); break; } } catch(\TypeError $e){ return $this->tfphp->getResponse()->responseJSON_CM(200, 1001061, "错误请求"); } // 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="text" name="email" /> </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/bindEmail/_send", method: "post", validations: [ {type:"empty", name:"email", msg:"请填写电子邮箱地址"} ], onSuccess: function (d) { if(d.errcode == 0){ document.location = '<% $TFReq->server->BASE_URI %>member/bindEmailOK.htm'; } 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
sendBindEmail
首先检查用户是否存在,不存在就报错。
接着检查邮箱是否被占用了,如果被占用了就报错。
最后就是要组织激活码邮件了,激活码邮件的正文包含一个链接,这个链接最后有一串激活码,激活码就是一个包含用户信息的数组经过序列化后使用AES加密生成的字符串了。由于我们设置了一个私钥,所以即使知道加密原理的人也不能解密我们的激活码。
我们的目的是要用户点击这个链接,打开激活页面,完成绑定邮箱操作。
接口控制器
doSend
这里就是当用户提交了电子邮箱地址后,我们使用sendBindEmail发送激活码邮件。
视图模板
HTML代码
这是要给只有一个输入框的表单,用户输入电子邮箱后点击提交按钮发送激活码邮件。
JS代码
这是一个标准的表单JS驱动程序,如果处理成功会自动转到发送激活码邮件成功页面。
效果
发送激活码邮件
激活码邮件已发送
因为激活码邮件包含链接,所以会被客户端软件当成垃圾邮件处理,没有办法了。
总结
今天福哥带着童鞋们完成了绑定邮箱第一部分功能——发送绑定邮箱激活码邮件的开发。这个功能涉及到的技术点很多,包括SMTP协议、AES算法、加密/解密处理等等。
下一课,福哥将带着大家实现绑定邮箱第二部分功能——验证激活码邮件的开发。