Laravel 发送邮件

标签: 编程学习 PHP学习

给博客添加邮件系统,博主太穷,没有使用三方的邮件推送服务,直接使用的阿里云企业邮箱 SMTP 发信,其实与腾讯、网易的 SMTP 发送邮件是一样的,就是登陆你的账户进行发送。

前置条件

首先需要有个备案域名,注册阿里、网易或者腾讯的企业邮箱(QQ个人邮箱也可以发,我选企业邮箱~.~),然后自行添加域名 MX 解析,直到生效,后面就是添加发信邮箱账号并配置密码,这里要注意一下,添加账号的时候要勾选SMTP和POP3,当然添加后设置也行

最后需要获取一下发件端口,我这里用的阿里云,下面贴出介绍地址

http://mailhelp.mxhichina.com/smartmail/detail.vm?knoId=5871700

这里不建议用 25 端口,因为阿里云服务器为了安全,关闭了 25 端口,想开启需要申请,挺麻烦。

配置发件信息

在 .env 文件中添加邮箱发件配置

MAIL_DRIVER=smtp
# 网易:smtp.ym.163.com;阿里:smtp.qiye.aliyun.com
MAIL_HOST=smtp.qiye.aliyun.com
# 发件端口,网易:994;阿里:465
MAIL_PORT=465
# 发件人邮箱帐号
MAIL_USERNAME=
# 发件人邮箱密码
MAIL_PASSWORD=
# 加密方式,因为 SMTP 没有使用 25 端口,所以此处填写 ssl
MAIL_ENCRYPTION=ssl
# 配置全局默认发件地址
MAIL_FROM_ADDRESS=
# 发件人名称
MAIL_FROM_NAME=

添加依赖函数库

这个服务基于 API 的驱动,所有的 API 驱动都需要 Guzzle HTTP 函数库,需要使用 Composer 包管理器安装它

composer require guzzlehttp/guzzle

创建邮件管理类及内容视图

php artisan make:mail RegisterMail --markdown=email.register

该命令会直接创建邮件管理文件 app/Mail/RegisterMail.php

class RegisterMail extends Mailable {
    use Queueable, SerializesModels;

    private $code;
    private $title;

    /**
     * Create a new message instance.
     *
     * @return void
     */
    public function __construct($title, $code)
    {
        $this->code = $code;
        $this->title = $title;
    }

    /**
     * Build the message.
     *
     * @return $this
     */
    public function build()
    {
        return $this->markdown('email.register', ['code'=>$this->code])->subject($this->title);
    }
}

视图文件resources/views/email/register.blade.php

@component('mail::message')

### 欢迎注册 Duicode 账号,您的邀请码是:

<br/>

# {{ $code }}

<br/>

##### 验证码有效期为 15 分钟, 请及时使用。

Thanks,<br>
{{ config('app.name') }}
@endcomponent

如果想修改 component 包含的文件内容,可以执行下面命令导出文件进行编辑,执行该命令后 resouces/views 下会多出 vendor/mail 目录

php artisan vendor:publish --tag=laravel-mail

测试发件

添加路由

Route::get('sendEmailCode', 'AuthController@sendCodeAction');

添加发件方法

public function sendCodeAction(Request $request) {
    //获取提交过来的邮箱
    $email = $request->get('email');
    if (empty(trim($email))) return ApiResponseBuilder::errorWithMessage(200,'请输入邮箱',200);

    //生成随机的四位数验证码
    $code = mt_rand(1000, 9999);

    //主要是这一句
    $result = Mail::to($email)->send(new RegisterMail('注册验证码', $code));
    if($result == null){
        //验证码存入缓存 有效期15分钟
        \Cache::put("email_code" . $email, $code, Carbon::now()->addMinute(15));
        $data = "验证码发送成功 ---" . $code;
        return ApiResponseBuilder::success($data);
    } else {
        return ApiResponseBuilder::errorWithMessage(200,'验证码失败',200);;
    }
}

防爆接口

为了防止接口被刷,我这里添加了云片行为验证(普通用户免费~.~),需要注册云片账号并进行实名认证,首先在前端添加验证码,在请求接口的时候添加云片验证后的token和authenticate,后端在发送邮件之前添加二次验证

public function sendCodeAction(Request $request) {
    //获取提交过来的邮箱
    $email = $request->get('email');
    if (empty(trim($email))) return ApiResponseBuilder::errorWithMessage(200,'请输入邮箱',200);

    // 获取云片验证数据
    $token          = $request->get('token');
    $authenticate   = $request->get('authenticate');
    if (empty(trim($token)) || empty(trim($authenticate))) return ApiResponseBuilder::errorWithMessage(200,'验证失败',200);

    // 云片二次验证
    $params = array();
    $params["authenticate"] =$authenticate;                             //用户验证通过后,返回的参数
    $params["token"] =$token;                                           //前端返回的 token
    $params["captchaId"] ='';                                           //验证应用 id
    $params["secretId"] ='';                                            //验证应用 secretId
    $secretKey = '';                                                    //验证应用 secretKey
    $params["version"] = "1.0";                                         //版本,固定值1.0
    $params["timestamp"] = sprintf("%d", round(microtime(true)*1000));  // 当前时间戳的毫秒值,如1541064141441
    $params["nonce"] = sprintf("%d", rand(1,99999));                    //随机正整数, 在 1-99999 之间
    ksort($params);                                                     // 参数排序
    $buff="";

    foreach($params as $key=>$value){
        $buff .=$key;
        $buff .=$value;
    }
    $buff .= $secretKey;
    //print_r($buff);
    $signature=md5($buff);
    $params["signature"] =$signature ;                                  //签名信息,见签名计算方法

    $url="https://captcha.yunpian.com/v1/api/authenticate";
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    /* 设置返回结果为流 */
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    /* 设置超时时间*/
    curl_setopt($ch, CURLOPT_TIMEOUT, 10);
    /* 设置通信方式 */
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/x-www-form-urlencoded'));
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
    $result = curl_exec($ch);
    if ($result['code'] != 0) {
        return ApiResponseBuilder::errorWithMessage(200, $result['msg'],200);
    }

    //生成随机的四位数验证码
    $code = mt_rand(1000, 9999);

    //主要是这一句
    $result = Mail::to($email)->send(new RegisterMail('注册验证码', $code));
    if($result == null){
        //验证码存入缓存 有效期5分钟
        \Cache::put("email_code" . $email, $code, Carbon::now()->addMinute(5));
        $data = "验证码发送成功 ---" . $code;
        return ApiResponseBuilder::success($data);
    } else {
        return ApiResponseBuilder::errorWithMessage(200,'验证码失败',200);;
    }
}

这就是目前搞的发送邮件流程了。