YII2 邮箱找回密码简单示例

邮箱找回密码简单示例

不可直接复制,都是伪代码!!

第一步:YII2配置邮箱

在backend/config/main-local.php 文档中添加邮箱设置
注意,邮箱设置的代码要在‘components’里面

$config = [
    'components' => [
        
/*-------------------配置mailer-----------------*/
        'mailer' => [
            'class' => 'yii\swiftmailer\Mailer',
            'useFileTransport' =>false,//这句一定有,false发送邮件,true只是生成邮件在runtime文件夹下,不发邮件
            'transport' => [
                'class' => 'Swift_SmtpTransport',
                'host' => 'smtp.163.com',               //每种邮箱的host配置不一样
                'username' => '******@163.com',
                'password' => '******',                 //邮箱是授权码,不是登录密码
                'port' => '25',
                'encryption' => 'tls',
            ],
            'messageConfig'=>[
                'charset'=>'UTF-8',
                'from'=>['me_dennis@163.com'=>'贝阳科技']       // 发件人名称
            ],
        ],
/*-------------------END-----------------*/

    ],
];

第二步:控制器中调用mailer类

在发送邮件的时候,comppose可以调用指定的邮件模版。
示例:Yii::$app->mailer->compose('resetPwdEmail', ['uid'=>$user->fuserid,'ptk' => $pwdtoken]);

  • 这个模版文件默认路径在 common/mail/resetPwdEmail.php
  • 第二个参数array,携带的参数
  • 和控制器加载视图的原理差不多
// 发送找回密码的邮件
    public function actionSendPasswordEmail()
    {
        $rtn = new ReturnValue();       // 这个是自定义的一个回复类
        $rtn -> code = -1;

        // 用户名和邮箱
        $username = $_POST['username'];
        $email = $_POST['email'];

        // 匹配用户名和邮箱
        $user = UserModel::findOne(['fusername'=>$username, 'femail'=>$email]);
        if (!$user) {
            $rtn -> message = '用户名和邮箱不匹配';
            return JsonUtils::encode($rtn);
        }

        // 生成password token , 有效期2天
        $pwdtoken = $this->makeptk($user);
        
        // 发送邮件,这里开始调用mailer
        $mail = Yii::$app->mailer->compose('resetPwdEmail', ['uid'=>$user->fuserid,'ptk' => $pwdtoken]);
        $mail->setTo($email);
        $mail->setSubject('密码找回邮件');          // 邮件主题
//        $mail->setTextBody('beyondscrren');       // 设置文本内容,调用模版可以不用这个
//        $mail->setHtmlBody('whatthisis');         // 设置HTML内容,调用模版可以不用这个
        if ($mail->send()) {
            $rtn -> code = 0;
            $rtn -> message = '邮件发送成功,请在48小时内进行密码重置操作!';
            return JsonUtils::encode($rtn);
        } else {
            $rtn -> message = '邮件发送失败!请重试';
            return JsonUtils::encode($rtn);
        }
    }
    
/*------------------好吧,回复类加上-------------------------------*/
/*----------------------ReturnValue----------------------------------*/
class ReturnValue
{
    //0:成功,-1失败
    public $code = 0;
    public $message = '';
    
    //返回给前台的记录数,Row count
    public $Total = 0;
    //记录数组
    public $Rows = array();
    //翻页对象
    public $otherData = '';
    
    //由于前台不能获取php服务器的根路径,此处传给前台js使用
    public $webRootUrl = '';
    public $ftpRootUrl = '';
    public $host = '';
    public $rt;

    public function __construct()
    {
        $this->webRootUrl = Yii::$app->baseUrl;
        $this->ftpRootUrl = FileUtils::getDBFileWebRoot();
        $this->host = Yii::app()->request->hostInfo;
    }
}

第三步:生成邮箱的链接密钥

这里面最关键的就是加密/解密函数 encrypt($str, 加/解密, $key)

  • 参数说明,'D' 是解密,其是加密
  • $key 为自己保存的key,类似salt
**先放出输出结果:**
加密后的字符串 $a = izATeQXrIzTiR4M83/qbkZ2mnzcNYIk8z+QfN/R2Kjojcw
$b = zhengneng6?time=1492063529
$c = 1492063529
$d = zhengneng6

注意: 用户名里面带有 '?time=' 会出Bug哦! 在注册和登录的时候,需要过滤特殊字符

$username = 'zhengneng6';
$str = $username.'?time='.time();
$key = '1490868820';    // 用户加密的salt

$a = encrypt($str,'E',$key);
echo $a,'<br/>';

$b = encrypt($a,'D',$key);
echo $b,'<br/>';

$c = substr($b,strpos($b, '?time=')+6);
echo $c,'<br/>';

$d = substr($b,0,strpos($b, '?time='));
echo $d;

function encrypt($string,$operation,$key=''){ 
    $key=md5($key); 
    $key_length=strlen($key); 
      $string=$operation=='D'?base64_decode($string):substr(md5($string.$key),0,8).$string; 
    $string_length=strlen($string); 
    $rndkey=$box=array(); 
    $result=''; 
    for($i=0;$i<=255;$i++){ 
           $rndkey[$i]=ord($key[$i%$key_length]); 
        $box[$i]=$i; 
    } 
    for($j=$i=0;$i<256;$i++){ 
        $j=($j+$box[$i]+$rndkey[$i])%256; 
        $tmp=$box[$i]; 
        $box[$i]=$box[$j]; 
        $box[$j]=$tmp; 
    } 
    for($a=$j=$i=0;$i<$string_length;$i++){ 
        $a=($a+1)%256; 
        $j=($j+$box[$a])%256; 
        $tmp=$box[$a]; 
        $box[$a]=$box[$j]; 
        $box[$j]=$tmp; 
        $result.=chr(ord($string[$i])^($box[($box[$a]+$box[$j])%256])); 
    } 
    if($operation=='D'){ 
        if(substr($result,0,8)==substr(md5(substr($result,8).$key),0,8)){ 
            return substr($result,8); 
        }else{ 
            return''; 
        } 
    }else{ 
        return str_replace('=','',base64_encode($result)); 
    } 
}

第四步:配置邮件模版的内容,也就是携带密钥的连接

<?php
use yii\helpers\Html;

/* @var $this yii\web\View */
/* @var $user common\models\User */

$resetLink = Yii::$app->urlManager->createAbsoluteUrl(['URL路径', 'uid'=>$uid, 'token' => $ptk]);
?>

<div class="password-reset">

    <p>hello ,</p>

    <p>Follow the link below to reset your password:</p>

    <p><?= Html::a(Html::encode($resetLink), $resetLink) ?></p>
    
</div>

第五步:验证密钥,重置密码

这一步的关键是decodeptk()

  • 这个函数里面,调用的是encrypt
public function actionResetPassword()
{
    // 接收参数
    if (empty($_GET['uid']) || empty($_GET['token'])) return false;
    
    $uid = intval($_GET['uid']);
    $token = $_GET['token'];
    
    $ptk = $this->decodeptk($uid, $token);  // 这一步调用
    
    if (!$ptk) return $this->render('pastDue',['type'=>1]);     // 加载报错页面
    // 先判断是否过期,有效期 2天
    $sendtime = substr($ptk, strpos($ptk, '?time=') + 6);
    if (time()-$sendtime>(3600*24*2)) return $this->render('pastDue',['type'=>2]);        // 加载报错页面

    return $this->render('resetPassword', [     // 加载重置密码页面
        'uid' => $uid,
        'token' => $token,
    ]);
}

加载重置密码的页面:
重置密码页面用表单提交新密码和密钥

<style>
    /* 表单样式 */
    /* 表单样式 */
    header{ margin: 0;padding: 10px 15px;background: #20a3dd;color: #fff;}
    .logo img{ width: 38%;margin: 0 auto;}
    .content input{ padding: 20px;margin: 20px 0; color: #8F8F8F; box-shadow: none;border: none; border-bottom:1px solid #D7D7D7;padding-left: 50px;font-size: 16px; }
</style>

<div class="body">
    <header>
        <h4 class="pname1 text-center"> 找回密码功能</h4>
    </header>

    <div>
        <h2>邮箱找回密码功能</h2>
    </div>
    <form action="editPassword.html" method="post" name="getpassword" >
        <div>
            <p>输入密码:<input name="password" type="password" placeholder="输入密码"></p>
            <p>再次输入:<input name="confirm" type="password" placeholder="确认密码"></p>
        </div>
        <div>
            <input name="uid" type="hidden" value="<?php echo $uid ?>">
            <input name="token" type="hidden" value="<?php echo $token ?>">
            <input name="submit" type="submit" value="确认提交">
        </div>
    </form>
</div>

最后一步,重置密码

public function actionEditPassword()
    {
        if (isset($_POST['uid']) && isset($_POST['token'])) {
            $uid = intval($_POST['uid']);
            $ptk = $_POST['token'];
        } 
        else throw new NotFoundHttpException();

        // 验证token
        $ptk = $this->decodeptk($uid, $ptk);
        if (!$ptk) return $this->render('pastDue',['type'=>1]);                         // uid和token不合法

        $sendtime = substr($ptk, strpos($ptk, '?time=') + 6);
        if (time()-$sendtime>(3600*24*2)) return $this->render('pastDue',['type'=>2]);  // 时间过期

        // 重置密码
        $password = $_POST['password'];
        $confirm = $_POST['confirm'];
        if ($password != $confirm) return false;    // 绕过前端验证的忽略

        $trans = DbUtils::beginTransaction();       // 事务
        try {
            $user = ByUsersTfansModel::findOne(['fuserid'=>$uid]);
            $newpsd = ByUsersTfansModel::setPassword($password,$user->fregtime);
            $user -> fpassword = $newpsd;
            if ($user->save()) {
                $trans -> commit();
                return $this->render('pastDue', ['type' => 0]);     //修改成功
            }
        } catch (Exception $e) {
            $trans -> rollBack();
            return $this->render('pastDue', ['type' => 1]);         // 修改失败
        }
    }

这个里面的pastDue,为修改后返回信息的页面。
自定义样式和type,根据不同的type返回不同的结果给用户。

posted @ 2017-04-14 14:05  风吹草歪  阅读(838)  评论(0编辑  收藏  举报