使用Python+Service Principal调用Microsoft Graph API发邮件报错求助
问题分析与解决方法
错误原因解析
1. /me 端点认证错误
服务主体采用应用权限认证流,而/me端点仅支持委派权限认证流(需代表具体用户登录),因此调用/me/sendMail会触发BadRequest错误。
2. 无效用户ID错误
users/{user_id}/sendMail中的user_id必须是Azure AD中已存在的Exchange Online邮箱用户标识,而非服务主体的client_id、tenant_id或组合值。服务主体本身没有邮箱,必须依托实际用户的邮箱完成邮件发送。
解决步骤
1. 获取合法用户标识
从Azure AD门户中获取一个拥有Exchange Online邮箱的用户的任意一种标识:
- 用户的SMTP邮箱地址(如
sender@yourcompany.com) - 用户的Object ID(在用户详情页面的「概述」标签下)
2. 修改Graph API端点
将代码中的graph_url替换为:
# 替换为实际的发件用户邮箱或Object ID sender_identity = "sender@yourcompany.com" graph_url = f"https://graph.microsoft.com/v1.0/users/{sender_identity}/sendMail"
3. 确认权限配置
确保服务主体已被授予应用权限类型的Mail.Send权限(而非委派权限),且权限范围为租户级(已完成管理员同意)。
修正后的完整代码
import requests import json import msal # 邮件详情 recipient_email = "receiver@example.com" subject = "Email subject" message_body = "Email message body" # 服务主体信息 client_id = "90xxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" client_secret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" tenant_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx" # 替换为实际的发件用户邮箱或Object ID sender_identity = "sender@yourcompany.com" graph_url = f"https://graph.microsoft.com/v1.0/users/{sender_identity}/sendMail" # 初始化客户端 app = msal.ConfidentialClientApplication( client_id=client_id, authority=f"https://login.microsoftonline.com/{tenant_id}", client_credential=client_secret, ) # 获取访问令牌 result = app.acquire_token_for_client(scopes=["https://graph.microsoft.com/.default"]) access_token = result.get("access_token") # 构建邮件内容 email_message = { "message": { "subject": subject, "body": { "contentType": "Text", "content": message_body, }, "toRecipients": [ { "emailAddress": { "address": recipient_email } } ] } } # 发送邮件 response = requests.post( graph_url, headers={ "Authorization": "Bearer " + access_token, "Content-Type": "application/json", }, json=email_message, # 直接用json参数,无需手动序列化 ) # 结果判断 if response.status_code == 202: print("Email sent successfully!") else: print(f"Failed to send email. Status code: {response.status_code}") print(response.text)
注:代码优化了请求参数,直接使用
json=email_message替代手动dumps后传入data,更简洁且避免序列化错误。
内容的提问来源于stack exchange,提问作者Beginner
相关产品推荐
相关产品推荐

