
本文详解如何避免将 JSON 字符串误当作文件路径传入 Google Auth 方法,通过 json.loads() 直接解析 Base64 解码后的环境变量内容,并调用 from_service_account_info() 正确初始化凭据。
本文详解如何避免将 json 字符串误当作文件路径传入 google auth 方法,通过 json.loads() 直接解析 base64 解码后的环境变量内容,并调用 from_service_account_info() 正确初始化凭据。
在 CI/CD 环境(如 CircleCI)中,常将 Google 服务账号密钥以 Base64 编码形式注入环境变量(例如 SERVICE_ACCOUNT),再于运行时解码并用于认证。但一个常见误区是:误将已解码的 JSON 字符串当作文件路径传给 from_service_account_file(),从而触发 OSError: [Errno 36] File name too long —— 实际上,Python 尝试把长达数千字符的 JSON 内容(含换行、引号、反斜杠)当成了非法超长文件名。
根本原因在于方法语义混淆:
-
from_service_account_file(filename):接收本地磁盘上的 JSON 文件路径(如"service-account.json"); -
from_service_account_info(info):接收已加载为 Python 字典的 JSON 数据(即dict类型,非字符串)。
你当前代码的问题在于:
service_account_file = json.dumps(os.environ['SERVICE_ACCOUNT_DECODED']) # ❌ 错误:对 JSON 字符串再次序列化 credentials = service_account.Credentials.from_service_account_file(service_account_file, scopes=SCOPES) # ❌ 传入的是字符串内容,不是路径
os.environ['SERVICE_ACCOUNT_DECODED'] 已是合法 JSON 格式的字符串(如 '{"type": "service_account", ...}'),直接 json.dumps() 会将其转为带双引号和转义符的嵌套字符串(如 '"{
\"type\": ...}"'),导致 from_service_account_file() 尝试打开这个畸形“文件名”,自然报错。
✅ 正确做法是跳过文件 I/O,直接解析 JSON 字符串为字典,并使用 from_service_account_info():
import json
import os
from google.oauth2 import service_account
# 假设 SERVICE_ACCOUNT_DECODED 已在环境变量中(CircleCI 中通过 base64 -di 解码后注入)
service_account_json_str = os.environ['SERVICE_ACCOUNT_DECODED']
# 解析 JSON 字符串为 Python 字典(关键步骤)
try:
service_account_info = json.loads(service_account_json_str)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in SERVICE_ACCOUNT_DECODED: {e}")
# 使用字典直接创建凭据(无需写入临时文件)
credentials = service_account.Credentials.from_service_account_info(
service_account_info,
scopes=['https://www.googleapis.com/auth/drive.file'] # 替换为实际所需 scopes
)
⚠️ 注意事项:
-
切勿对已解码的 JSON 字符串调用
json.dumps()—— 这会导致双重编码,破坏数据结构; - 确保
SERVICE_ACCOUNT_DECODED确实是有效 JSON(无多余空格、编码错误),建议在 CI 中添加校验步骤; - 若必须使用
from_service_account_file(),可将内容写入临时文件(不推荐,增加 I/O 和清理负担):import tempfile with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as f: f.write(service_account_json_str) temp_path = f.name credentials = service_account.Credentials.from_service_account_file(temp_path, scopes=SCOPES) os.unlink(temp_path) # 记得清理 - 推荐始终使用
from_service_account_info()处理内存中凭据,更安全、高效且符合云原生实践。
总结:环境变量中存储的是 JSON 字符串,认证时应走「解析 → 字典 → from_service_account_info」路径,而非「误当路径 → from_service_account_file」。这是 Python 处理动态凭据的标准范式,适用于 GitHub Actions、GitLab CI、Cloud Build 等各类平台。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











