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

如何为Office 365脚本仅授予自身邮箱的访问权限?

解决方案:仅访问自身邮箱的Graph API邮件附件下载脚本

问题分析

你当前使用的是客户端凭证授权流(Client Credentials Flow),这种模式下的Mail.Read属于应用权限,默认需要管理员同意,且会授予访问所有邮箱的权限——这就是你遇到Access is denied错误的核心原因。而Graph Explorer使用的是授权码流(Authorization Code Flow),属于委派权限,仅需你个人登录同意即可访问自身邮箱,但临时令牌无法长期复用。

调整方案:改用带刷新令牌的授权码流

要实现长期、安全地访问自身邮箱,且无需管理员同意,需切换到授权码流并启用令牌缓存(自动用刷新令牌续期访问令牌)。

步骤1:Azure AD应用配置

  • 注册应用时,设置重定向URI为http://localhost:8000(或本地可访问的地址)
  • 添加委派权限:Mail.Read(注意是「委派权限」而非「应用权限」),该权限无需管理员同意,你个人登录即可授权

步骤2:修改后的可运行代码

import msal
import json
import requests
import os

# 配置参数
CLIENT_ID = '你的客户端ID'
TENANT_ID = '你的租户ID'
REDIRECT_URI = 'http://localhost:8000'
SCOPE = ['https://graph.microsoft.com/Mail.Read']

# 令牌缓存文件,用于持久化刷新令牌
TOKEN_CACHE_FILE = 'token_cache.json'

def load_token_cache():
    if os.path.exists(TOKEN_CACHE_FILE):
        with open(TOKEN_CACHE_FILE, 'r') as f:
            return msal.SerializableTokenCache(json.load(f))
    return msal.SerializableTokenCache()

def save_token_cache(cache):
    with open(TOKEN_CACHE_FILE, 'w') as f:
        json.dump(cache.serialize(), f)

def get_access_token():
    cache = load_token_cache()
    app = msal.PublicClientApplication(
        client_id=CLIENT_ID,
        authority=f'https://login.microsoftonline.com/{TENANT_ID}',
        token_cache=cache
    )

    # 优先从缓存获取有效令牌
    accounts = app.get_accounts()
    if accounts:
        result = app.acquire_token_silent(SCOPE, account=accounts[0])
        if result:
            save_token_cache(cache)
            return result

    # 无有效令牌时,触发浏览器登录授权
    result = app.acquire_token_interactive(SCOPE, redirect_uri=REDIRECT_URI)
    if 'access_token' in result:
        save_token_cache(cache)
        return result
    else:
        raise Exception(f"令牌获取失败: {result.get('error_description')}")

# 获取访问令牌
token_result = get_access_token()
access_token = token_result['access_token']

# 邮件筛选条件(注意日期格式需带时区)
date_received = '20**-**-**T00:00:00Z'
mail_subject = '目标邮件主题'
mail_sender = 'sender@example.com'

# 使用/me端点访问当前登录用户的邮箱(无需指定用户ID)
url = f"https://graph.microsoft.com/v1.0/me/messages?$filter=startswith(from/emailAddress/address, '{mail_sender}') and subject eq '{mail_subject}' and receivedDateTime ge {date_received}"

headers = {
    "Authorization": f"Bearer {access_token}",
    "Content-Type": "application/json"
}

# 获取符合条件的邮件列表
response = requests.get(url, headers=headers)
response.raise_for_status()
data = response.json()

# 下载邮件附件
def get_email_attachment(message_id):
    url = f"https://graph.microsoft.com/v1.0/me/messages/{message_id}/attachments"
    response = requests.get(url, headers=headers)
    response.raise_for_status()
    return response.json()

for d in data["value"]:
    mes_id = d['id']
    attachments = get_email_attachment(mes_id)
    for attachment in attachments['value']:
        attachment_name = attachment['name']
        attachment_id = attachment['id']
        print(f"正在下载: {attachment_name}")

        # 附件下载端点需追加/$value获取二进制内容
        download_url = f"https://graph.microsoft.com/v1.0/me/messages/{mes_id}/attachments/{attachment_id}/$value"
        response = requests.get(download_url, headers=headers)
        response.raise_for_status()

        # 保存附件到本地
        with open(attachment_name, 'wb') as f:
            f.write(response.content)
        print(f"{attachment_name} 保存完成")

关键说明

  • 授权码流优势:第一次运行会弹出浏览器让你登录授权,后续运行自动读取缓存的刷新令牌续期访问令牌,无需重复登录
  • 权限范围控制:委派权限Mail.Read仅允许访问当前登录用户的邮箱,不会触及其他用户数据,无需管理员审批
  • 端点优化:用/me代替/users/{mail_user},自动关联当前登录用户,简化代码
  • 日期格式要求:Graph API的receivedDateTime参数必须带时区(如2024-05-01T00:00:00Z),否则会触发格式错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 15:32:32