OAuth2 连接 Outlook 的 IMAP / POP3:实现方法与原理

前言

除了 Microsoft Graph,Outlook 邮箱也可以通过传统邮件协议读取,例如 IMAP 和 POP3。本文结合一个 Python 示例脚本,说明如何使用 OAuth2 的 access_token 配合 XOAUTH2 认证登录 Outlook 邮箱,并解释这套接入方式背后的原理。

整体流程

这个脚本整体分成三段:

  1. 用 refresh_token 向微软换取 access_token

  2. 生成 XOAUTH2 认证串

  3. 分别连接 IMAP 和 POP3 服务,读取邮箱数据

对应关系如下:

  • OAuth2 负责颁发令牌

  • XOAUTH2 负责把令牌包装成邮件协议可识别的认证格式

  • IMAP / POP3 负责真正访问邮件服务

第一步:刷新 access_token

脚本先请求微软令牌接口:

import requestsdef get_access_token(refresh_token: str, client_id: str) -> str:
    data = {        "client_id": client_id,        "grant_type": "refresh_token",        "refresh_token": refresh_token,
    }
    resp = requests.post(        "https://login.microsoftonline.com/consumers/oauth2/v2.0/token",
        data=data,
        timeout=15,
    )
    resp.raise_for_status()    return resp.json()["access_token"]

这一步和 Graph API 的思路一致:

  • refresh_token 用来续期

  • access_token 用来真正访问资源

区别只是后面访问的不是 Graph,而是 IMAP / POP3 服务。

第二步:生成 XOAUTH2 认证串

脚本里有一个很关键的方法:

def generate_auth_string(user: str, token: str) -> str:    return f"user={user}\x01auth=Bearer {token}\x01\x01"

这里的 \x01 是控制字符 SOH,XOAUTH2 认证内容本质上是一个特殊格式的字符串。服务端收到后,会把其中的:

  • 用户邮箱

  • Bearer Token

拆出来校验。

第三步:连接 IMAP

原脚本使用 imaplib.IMAP4_SSL 连接 Outlook:

import imaplibdef connect_imap(email_addr: str, access_token: str):
    mail = imaplib.IMAP4_SSL("outlook.live.com")
    mail.authenticate("XOAUTH2", lambda _: generate_auth_string(email_addr, access_token))    return mail

认证成功以后,脚本继续:

  • 选择 INBOX 收件箱

  • 选择 Junk 垃圾箱

  • 执行 search(None, 'ALL')

  • 再逐封 fetch(..., '(RFC822)')

这里抓到的是完整原始邮件内容,也就是 RFC822 格式报文。

邮件正文是怎么解析出来的

原脚本使用 Python 内置 email 模块解析返回结果:

import emaildef fetch_email_body(mail, item):
    status, msg_data = mail.fetch(item, "(RFC822)")    for response_part in msg_data:        if isinstance(response_part, tuple):
            msg = email.message_from_bytes(response_part[1])
            subject = msg.get("Subject")
            sender = msg.get("From")
            ...

后面又进一步判断:

  • 如果是多部分邮件 multipart

  • 就遍历每个 part

  • 找到 text/plain 且不是附件的部分

  • 再解码正文

这一步其实就是在做 MIME 报文拆解。

为什么比 Graph API 麻烦

因为 IMAP 返回的是原始邮件内容,不是已经整理好的 JSON。开发者必须自己处理:

  • 主题编码

  • 发件人字段

  • 多部分正文

  • 附件判断

  • 字符集解码

也正因如此,传统协议更灵活,但实现成本也更高。

POP3 部分是怎么做的

脚本里的 POP3 连接方式是:

import base64import poplibdef connect_pop3(email_addr: str, access_token: str):
    server = poplib.POP3_SSL("outlook.live.com")
    auth = generate_auth_string(email_addr, access_token)
    encoded = base64.b64encode(auth.encode("utf-8")).decode("utf-8")
    server._shortcmd("AUTH XOAUTH2")
    server._shortcmd(encoded)    return server

这里比 IMAP 多了一步 Base64 编码,因为 POP3 的认证交换格式通常需要把认证串编码后发给服务端。

脚本最后调用 server.list() 打印邮件列表,说明连接与认证已经成功。

IMAP 和 POP3 的区别

虽然都能收邮件,但它们适用场景不同:

IMAP

  • 支持文件夹

  • 支持服务端搜索

  • 支持已读、未读、删除等状态同步

  • 更适合做完整邮箱客户端或邮件同步程序

POP3

  • 协议更简单

  • 更偏向“下载邮件”

  • 文件夹和状态能力弱很多

  • 更适合做轻量拉取任务

如果要读取收件箱、垃圾箱、按条件检索、保留服务端状态,IMAP 会更合适。

这类脚本适合什么场景

在合法授权前提下,这类脚本适合:

  • 自建邮件备份工具

  • 企业内部告警邮箱轮询

  • 邮件工单自动分发

  • 兼容旧系统的邮箱同步服务

如果只是读取邮件列表、主题、发件人等基础信息,Graph API 更省事;如果要兼容旧邮件生态、处理原始 MIME、保留传统协议能力,IMAP / POP3 仍然有价值。

原脚本里可以优化的点

1. 敏感信息不要直接打印

原脚本会打印:

  • 响应内容

  • 新 refresh_token

  • access_token

  • 邮箱地址

这些都不适合保留在正式环境日志里。

2. 字符编码要更稳妥

邮件正文和主题经常存在多种编码,建议:

  • 使用更安全的兜底解码

  • 对异常编码单独处理

  • 不要默认所有正文都能直接 .decode() 成功

3. POP3 不建议依赖私有方法

原脚本使用了 server._shortcmd(),这是库的私有方法。测试脚本里能跑,但长期维护时可读性和稳定性都一般,更适合封装成清晰的认证流程。

4. 建议增加超时与错误分类

真实项目里应补上:

  • 请求超时

  • 认证失败

  • 令牌过期

  • 邮箱权限不足

  • 网络抖动重试

总结

这类 Outlook IMAP / POP3 接入脚本,本质上是把现代 OAuth2 令牌体系和传统邮件协议通过 XOAUTH2 连接起来:

  • 微软 OAuth2 负责发令牌

  • IMAP / POP3 负责提供邮件访问能力

  • Python email 模块负责解析原始报文

如果你需要兼容传统邮箱协议栈,或者必须处理原始邮件内容,那么这套实现依然很实用。只是相比 Graph API,它更底层,也更考验你对邮件协议和 MIME 结构的理解。

南图
南图管理员

  • 声明:本文由南图于2026-03-30发表在南图博客 - 优质稀缺资源发布平台,努力创造一个顶级资源站,如有疑问,请联系我们。
  • 本文地址:https://www.nta6.com/post/705.html
上一篇:Microsoft Graph API 拉取 Outlook 邮件的实现方法与原理
下一篇:点击网站logo刷新

留言评论

1条留言

评论列表

南图
1
2026-03-31 03:51:08 回复
取消
扫码支持