使用Python通过Microsoft Graph下载用户头像并关联vCard时遇402错误求助
解决Microsoft Graph获取用户头像的HTTP 402错误及完整实现步骤
先排查HTTP 402错误的核心问题
HTTP 402通常标记为"需要付费",但在Microsoft Graph场景下,更大概率是你的令牌请求参数或配置有误:
- 令牌请求URL错误:必须使用Azure AD标准令牌端点,格式为
https://login.microsoftonline.com/{你的租户ID}/oauth2/v2.0/token - scope参数格式错误:需指定合法的Graph权限范围,比如获取所有用户头像需要
https://graph.microsoft.com/User.Read.All - Content-Type错误:POST请求获取令牌时,必须设置为
application/x-www-form-urlencoded - grant_type不匹配:使用客户端凭证流时,grant_type应为
client_credentials;使用用户名密码流则为password,需根据认证方式调整 - 许可证限制:部分用户头像功能需要对应的Office 365/Microsoft 365许可证,若租户无授权也可能触发402
完整实现步骤(获取令牌 → 拉取用户列表 → 下载头像 → 关联vCard)
以下是修正后的完整代码,包含所有关键流程:
import requests import vobject # 需先安装:pip install vobject import base64 # 1. 获取Microsoft Graph访问令牌 tenant_id = "你的租户ID" client_id = "你的客户端ID" client_secret = "你的客户端密钥" token_url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token" token_data = { "grant_type": "client_credentials", "client_id": client_id, "client_secret": client_secret, "scope": "https://graph.microsoft.com/User.Read.All" } token_response = requests.post(token_url, data=token_data) token_response.raise_for_status() # 主动抛出HTTP错误,便于调试 access_token = token_response.json()["access_token"] # 2. 拉取租户内所有用户基础信息 request_headers = { "Authorization": f"Bearer {access_token}", "Content-Type": "application/json" } users_list_url = "https://graph.microsoft.com/v1.0/users?$select=id,displayName,mail" users_response = requests.get(users_list_url, headers=request_headers) users_response.raise_for_status() users = users_response.json()["value"] # 3. 遍历用户下载头像并完善vCard for user in users: user_id = user["id"] display_name = user["displayName"] email = user.get("mail", "") # 下载用户头像(size参数可调整:48/64/120/240/480,对应不同尺寸) avatar_url = f"https://graph.microsoft.com/v1.0/users/{user_id}/photo/$value" avatar_response = requests.get(avatar_url, headers=request_headers) # 处理用户无头像的情况 if avatar_response.status_code == 404: print(f"用户 {display_name} 未设置头像,跳过") continue avatar_response.raise_for_status() # 保存本地头像文件 avatar_filename = f"{user_id}_avatar.jpg" with open(avatar_filename, "wb") as f: f.write(avatar_response.content) print(f"已下载用户 {display_name} 的头像:{avatar_filename}") # 4. 生成并完善vCard vcard = vobject.vCard() vcard.add("fn").value = display_name if email: vcard.add("email").value = email vcard.email.type_param = "INTERNET" # 将头像嵌入vCard(Base64编码) avatar_base64 = base64.b64encode(avatar_response.content).decode("utf-8") photo_field = vcard.add("photo") photo_field.value = avatar_base64 photo_field.type_param = "JPEG" photo_field.encoding_param = "b" # 保存vCard文件 vcard_filename = f"{user_id}.vcf" with open(vcard_filename, "w", encoding="utf-8") as f: f.write(vcard.serialize()) print(f"已生成用户 {display_name} 的vCard:{vcard_filename}")
关键注意事项
- 确保你的Azure AD应用已被授予
User.Read.All的应用权限(而非委派权限),并完成管理员同意 - 若使用用户名密码流(grant_type=password),需将scope改为
https://graph.microsoft.com/User.Read.All offline_access,同时添加username和password参数 - 若仍出现402错误,需检查租户是否拥有Microsoft 365商业版/企业版等包含头像功能的许可证
内容的提问来源于stack exchange,提问作者RRM
相关产品推荐
相关产品推荐

