You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用OAuth令牌通过Office 365发送简单邮件时遇SMTP连接超时问题求助

使用OAuth令牌通过Office 365发送简单邮件时遇SMTP连接超时问题求助

看起来你在Symfony环境下用OAuth2客户端凭证模式成功拿到了令牌,但通过SMTP发送邮件时一直遇到连接超时。我帮你梳理几个关键排查点和正确的实现方案:

1. 先解决SMTP连接超时的核心问题

smtp.office365.com:587超时最常见的原因是服务器出站端口587被限制,先优先排查这个:

  • 登录部署代码的服务器,用命令测试连通性:
    telnet smtp.office365.com 587
    # 或者用nc
    nc -zv smtp.office365.com 587
    

如果连不上,直接找运维团队开放服务器防火墙/安全组的出站587端口,这是超时的最主要诱因。

2. Symfony Mailer正确适配OAuth2 SMTP的方式

你当前代码里手动给Email加Authorization header的做法是错误的——Symfony Mailer的SMTP传输需要专门的OAuth2认证器处理会话认证,而非直接给邮件头加令牌。修改代码试试:

public function emailTest(MailerInterface $mailer, HttpClientInterface $httpClient)
{
    // 1. 获取OAuth2令牌(保留你这部分正确的代码)
    $provider = new GenericProvider([
        'clientId'                => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
        'clientSecret'            => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
        'urlAuthorize'            => 'https://login.microsoftonline.com/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/oauth2/v2.0/authorize',
        'urlAccessToken'          => 'https://login.microsoftonline.com/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/oauth2/v2.0/token',
        'urlResourceOwnerDetails' => '',
    ]);

    $guzzleClient = new \GuzzleHttp\Client(['verify' => false]);
    $provider->setHttpClient($guzzleClient);

    try {
        // 注意:SMTP需要的scope是Outlook的,不是Graph的!
        $accessToken = $provider->getAccessToken('client_credentials', [
            'scope' => 'https://outlook.office365.com/.default',
        ]);
    } catch (IdentityProviderException $e) {
        dump($e->getMessage());
        dump($e->getResponseBody());
        exit();
    }

    // 2. 配置SMTP传输+OAuth2认证器
    $authenticator = new \Symfony\Component\Mailer\Transport\Smtp\Auth\OAuth2Authenticator(
        $httpClient,
        $accessToken->getToken()
    );

    // 初始化带OAuth2认证的SMTP传输
    $transport = Transport::fromDsn(
        'smtp://smtp.office365.com:587?encryption=tls&auth_mode=oauth2',
        $httpClient
    );
    $transport->setAuthenticator($authenticator);

    $mailer = new Mailer($transport);

    // 3. 创建并发送邮件
    $email = (new Email())
        ->from('XXXX@XXXX.XXXX')
        ->to('YYYYY@YYYYY.YYYY')
        ->subject('Test-E-Mail')
        ->text('Dies ist eine Test-E-Mail.');

    $mailer->send($email);

    return new Response('E-Mail erfolgreich gesendet!');
}

关键注意点:

  • Scope必须替换:SMTP需要https://outlook.office365.com/.default,而非你之前用的Graph scope,否则令牌没有SMTP发送权限。
  • 不要手动加邮件头:OAuth2认证器会自动处理SMTP会话的XOAUTH2认证流程,无需手动拼接令牌到邮件头。

3. 确认Azure AD应用的权限配置

确保你的Azure AD应用已经配置了正确的应用权限:

  1. 登录Azure门户 → 找到目标应用 → 「API权限」
  2. 添加权限 → 选择「Office 365 Exchange Online」
  3. 选择「应用权限」→ 勾选SMTP.Send → 点击「授予管理员同意」

4. 更可靠的替代方案:用Microsoft Graph API发送

如果SMTP端口一直无法开放,直接用Graph API发送邮件是更简洁的方案,无需依赖587端口:

// 拿到令牌后调用Graph API
$response = $httpClient->request('POST', 'https://graph.microsoft.com/v1.0/users/XXXX@XXXX.XXXX/sendMail', [
    'headers' => [
        'Authorization' => 'Bearer ' . $accessToken->getToken(),
        'Content-Type' => 'application/json',
    ],
    'json' => [
        'message' => [
            'subject' => 'Test-E-Mail',
            'body' => [
                'contentType' => 'Text',
                'content' => 'Dies ist eine Test-E-Mail.'
            ],
            'toRecipients' => [
                [
                    'emailAddress' => [
                        'address' => 'YYYYY@YYYYY.YYYY'
                    ]
                ]
            ]
        ],
        'saveToSentItems' => true
    ]
]);

if ($response->getStatusCode() === 202) {
    return new Response('E-Mail erfolgreich über Graph API gesendet!');
} else {
    dump($response->getContent());
    return new Response('Fehler beim Senden');
}

这个方案只需要服务器能访问https://graph.microsoft.com,权限用你原本的https://graph.microsoft.com/.default即可,只要应用配置了Mail.Send应用权限。

备注:内容来源于stack exchange,提问作者Gunnar Bock

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.20 07:04:30