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

Office365启用MFA后用应用密码做SMTP认证是否需要连接器?

Office365 SMTP 应用密码认证失败(535 5.7.139)排查方案

首先澄清几个误区:

  • 你看到的SMTPSecure: 'tls'、SMTPAuth: true是其他邮件客户端(比如PHPMailer、Nodemailer)的配置项,Python的smtplib不需要手动传这两个参数:你代码里调用starttls()后再执行login(),本身就已经实现了TLS加密传输、启用SMTP认证的逻辑,这部分原有逻辑没有问题。
  • 不需要配置SMTP Connector,这个组件是Exchange Online用来做跨组织邮件流路由的,和客户端SMTP提交认证没有关系,你的判断正确。

按以下优先级排查即可解决问题:

  • 第一优先级排查租户/邮箱的认证策略配置
    应用密码走的是SMTP基本认证流程,触发5.7.139最常见的原因是认证请求被策略拦截:
    1. 确认租户没有开启全局安全默认值(Security Defaults),也没有配置条件访问策略全局阻止旧式认证——这两个配置开启的情况下,哪怕单个邮箱开了SMTP AUTH,应用密码的认证请求也会被直接拦截。
    2. 不要只检查租户全局的SMTP AUTH开关,必须到Exchange管理中心找到对应发信邮箱,在「邮箱功能-电子邮件应用」配置项里,单独确认「已启用经过身份验证的SMTP」是勾选状态。微软现在默认会对开启MFA的新邮箱单独关闭这个开关,全局开启不代表单个邮箱生效。
    3. 确认你用的应用密码是给这个开启MFA的账户正确生成的:Office365的应用密码是16位随机字符串,生成时只会完整显示一次,复制时漏1-2位字符就会直接报认证失败。
  • 第二优先级修正代码的握手逻辑
    Office365的部分SMTP节点对EHLO握手校验比较严格,你原有代码缺少TLS握手前后的EHLO声明,会被部分节点判定为非法客户端。另外建议用上下文管理器自动处理连接释放,避免连接泄漏,修正后的可运行代码如下:
from pathlib import Path
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders

def send_mail(recipients: str, subject: str, body: str, attachments: list = None, cc: str = None):
    credentials = get_credentials()
    smtp_srv_name = "smtp.office365.com"
    # 上下文管理器自动关闭连接,无需手动调用close()
    with smtplib.SMTP(smtp_srv_name, 587, timeout=10) as smtp_server:
        # 排查问题时可打开调试日志,查看完整握手交互过程
        # smtp_server.set_debuglevel(1)
        smtp_server.ehlo()
        smtp_server.starttls()
        smtp_server.ehlo() # TLS握手后必须重新发起EHLO声明
        smtp_server.login('你的发信邮箱全地址', mc_decrypt(credentials['notices_email_pw'], CRYPT_KEY))

        msg = MIMEMultipart()
        msg['Subject'] = subject
        msg['From'] = '你的发信邮箱全地址'
        msg['To'] = recipients
        if cc:
            msg['Cc'] = cc
        # 指定utf-8编码,避免中文正文/主题乱码
        msg.attach(MIMEText(body, 'plain', 'utf-8'))

        if attachments:
            for attachment in attachments:
                with open(attachment, 'rb') as attachment_fileobject:
                    part = MIMEBase('application', 'octet-stream')
                    part.set_payload(attachment_fileobject.read())
                    encoders.encode_base64(part)
                    part.add_header('Content-Disposition', f"attachment; filename={Path(attachment).name}")
                    msg.attach(part)
        
        # 合并收件人、抄送人列表,否则抄送不会实际发出
        all_recipients = [addr.strip() for addr in recipients.split(',')]
        if cc:
            all_recipients += [addr.strip() for addr in cc.split(',')]
        smtp_server.sendmail(msg['From'], all_recipients, msg.as_string())

补充:如果以上配置都确认无误还是报错,直接让租户全局管理员到Azure AD的登录日志里,筛选该邮箱的失败登录记录,日志会明确标注拦截原因(密码错误/策略拦截/功能未开启),不需要盲目改配置。你当前使用的587端口+STARTTLS 1.2的连接方式完全符合Office365的要求,不需要调整连接参数。

内容的提问来源于stack exchange,提问作者evan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 01:27:18