返回

解决Laravel使用Gmail SMTP发送邮件失败问题(GoDaddy服务器)

php

Laravel 使用 Gmail SMTP 发送邮件失败:Failed to authenticate on SMTP server

最近把项目部署到 GoDaddy 服务器上时, 用 Gmail SMTP 发送邮件遇到了问题。本地环境一切正常, 到了线上就报 "Failed to authenticate on SMTP server with username ... using 2 possible authenticators" 的错误。 这篇文章就记录一下排查和解决这个问题的过程。

一、问题

本地开发时, 使用 Gmail SMTP 服务发送邮件没问题。但是,部署到 GoDaddy 服务器后,尝试发送邮件时,Laravel 抛出以下异常:

Swift_TransportException Failed to authenticate on SMTP server with username "[email protected]" using 2 possible authenticators

报错信息显示,在使用给定的用户名和两种可能的验证方式尝试登录 SMTP 服务器时,身份验证失败了。

二、问题原因分析

这个错误通常和以下几个方面有关:

  1. Gmail 账户设置: 可能是 Google 账户的安全设置阻止了应用程序的登录尝试。
  2. 网络环境差异: 本地和服务器的网络环境不同,可能导致连接或认证问题。 GoDaddy服务器可能有特殊的网络配置或限制。
  3. SMTP 配置错误: .env 文件或者 mail.php 配置文件中的 SMTP 设置不正确。
  4. 密码问题: 有可能是密码包含一些特殊字符。
  5. GoDaddy 服务器限制: GoDaddy 可能阻止了某些端口或对某些 SMTP 服务器的访问。

三、解决方案

针对上面分析的可能原因, 我尝试了下面几个方法来解决。

1. 检查 Gmail 账户设置

首先要排除 Google 账户设置导致的问题. 虽然问题说"less secure app turned ON",但还是值得再次确认以下几点。

  • 开启“允许不够安全的应用” : 以前Google 账户有个选项是“允许不够安全的应用访问”。 虽然这个选项现在大部分情况下找不到了,但是还是需要确保相关安全设置是允许的。 如果实在找不到,可以跳过。

  • 启用两步验证并生成应用专用密码 : 对于开启了两步验证的 Google 账户,直接使用账户密码进行 SMTP 验证是不行的。 需要在 Google 账户设置中生成一个“应用专用密码”,然后用这个专用密码替换 .env 文件中的 MAIL_PASSWORD

    • 访问你的 Google 账户。
    • 选择“安全性”。
    • 在“登录 Google”下,选择“两步验证”。
    • 在页面底部,选择“应用密码”。
    • 输入一个帮助你记住应用密码用在哪里的名称 (例如 "Laravel App"),然后选择“生成”。
    • 将生成的应用密码复制并替换掉 .env 文件中的 MAIL_PASSWORD
  • Google 账户的设备活动 : 检查下Google账户中最近的设备活动, 看是否有不正常的登录尝试被阻止。 如果有, 授权允许即可。

2. 检查 .env 和 mail.php 配置

确认.env 文件和 config/mail.php 文件中的 SMTP 设置是正确的:

.env 示例

MAIL_MAILER=smtp
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=[email protected]
MAIL_PASSWORD=你的应用专用密码或邮箱密码
MAIL_ENCRYPTION=tls
MAIL_FROM_ADDRESS=[email protected]
MAIL_FROM_NAME="你的应用名称"

config/mail.php 示例 (一般无需修改,除非有特殊需求)

return [
    'default' => env('MAIL_MAILER', 'smtp'),
    'mailers' => [
        'smtp' => [
            'transport' => 'smtp',
            'host' => env('MAIL_HOST', 'smtp.gmail.com'),
            'port' => env('MAIL_PORT', 587),
            'encryption' => env('MAIL_ENCRYPTION', 'tls'),
            'username' => env('MAIL_USERNAME'),
            'password' => env('MAIL_PASSWORD'),
            'timeout' => null,
        ],
    ],

    'from' => [
        'address' => env('MAIL_FROM_ADDRESS', '[email protected]'),
        'name' => env('MAIL_FROM_NAME', '你的应用名称'),
    ],
    'markdown' => [
            'theme' => 'default',
    
            'paths' => [
                resource_path('views/vendor/mail'),
            ],
    ],

];

注意事项:

  • MAIL_MAILER 在 Laravel 较新版本中应使用 smtp,而不是 MAIL_DRIVER
  • 确保 MAIL_USERNAMEMAIL_FROM_ADDRESS 使用相同的邮箱地址。
  • 使用应用专用密码的时候, 直接粘贴复制生成的密码, 避免输入错误。
  • 修改配置后, 重启服务和清除缓存:
        php artisan config:clear
        php artisan cache:clear
    

3. 使用 Google 的 “允许访问您的 Google 帐号” 功能

在配置完以上设置, 并在服务器上触发一次邮件发送后, 有时候Google 会阻止这次登陆尝试, 需要手动允许这次登陆。

可以查看 Gmail 收件箱是否有 Google 发来的安全提醒邮件,按照邮件指示操作。 或访问以下链接:

https://accounts.google.com/b/0/DisplayUnlockCaptcha

点击“继续”按钮, 允许应用程序访问你的 Google 账户。然后再次尝试发送邮件。

4. 检查服务器端口和防火墙

有时候问题不在代码, 而是服务器的网络设置。

  • 测试端口连通性: 尝试从服务器 telnet 到 Gmail 的 SMTP 服务器,检查端口是否开放:

    telnet smtp.gmail.com 587
    

    如果连接失败,说明 587 端口被阻止。 这种情况需要联系 GoDaddy 的技术支持,让他们开放 587 端口。

  • 尝试端口 465: 如果 587 端口有问题, 可以试试 465 端口 (SMTPS),并将 .env 文件中的 MAIL_ENCRYPTION 改为 ssl

    MAIL_PORT=465
    MAIL_ENCRYPTION=ssl
    

    记得修改配置后,清除配置缓存.

        php artisan config:clear
        php artisan cache:clear
    

5. 尝试其他 SMTP 服务商

如果以上方法都无法解决, 还可以考虑使用其他 SMTP 服务提供商, 比如 SendGrid, Mailgun, Amazon SES 等。 这些服务通常提供更可靠的邮件发送服务,并且有更详细的文档和技术支持。 更换其他SMTP服务商,需要对应修改.env 文件的配置.

6. 特殊密码字符的处理 (进阶)

如果邮箱密码中包含特殊字符(如 #$& 等),这些字符在 .env 文件中可能需要进行转义。

有两种处理方式:

  • 使用引号: 将密码用双引号或单引号括起来。

    MAIL_PASSWORD="your#password$here"
    
  • 转义特殊字符 : 使用反斜杠 \ 对特殊字符进行转义. (这种方式较为麻烦, 不推荐)

7. 使用日志调试 (进阶)

如果还是找不到具体原因, 可以打开 Laravel 的详细日志记录,查看更具体的错误信息。

  1. .env 文件中的 APP_DEBUG 设置为 true。 (注意:生产环境不建议开启debug, 排查问题后记得改回来!

  2. 设置 MAIL_LOG_CHANNEL,记录详细的邮件发送日志。
    .env文件中添加:

     MAIL_LOG_CHANNEL=mail
    

    config/logging.php 中的channels下, 添加mail channel:

        'channels' => [
               // ...其他 channels
            'mail' => [
                    'driver' => 'single',
                    'path' => storage_path('logs/mail.log'),
                    'level' => 'debug',
            ],
        ],
    
    
  3. 再次触发邮件发送,然后查看 storage/logs/mail.log 文件,看看是否有更详细的错误信息,帮助定位问题。