Docker部署的EduInsights项目Google API令牌过期问题求解决方案
问题描述
我正在为学校开发名为EduInsights的学期成绩管理网站,需要调用Google Drive和Sheets API完成两项操作:
- 将成绩导出至电子表格
- 提取Drive文件夹中PDF文件内的成绩数据
已在GCP控制台创建凭证并下载为.json文件,首次授权应用后生成的token.json文件会在几天后过期,必须删除该文件并重新授权。
由于应用已Docker化,且仅固定使用同一个Google账号调用API,希望找到一种方案,实现令牌一旦授权成功就长期有效,避免从Docker容器跳转至授权链接的复杂操作。
以下是当前使用的authenticate.py代码:
import os from dotenv import load_dotenv from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build from googleapiclient.errors import HttpError # Get the absolute path to the directory containing this Python script (alembic folder) current_dir = os.path.dirname(os.path.abspath(__file__)) print(f"Current directory: {current_dir}", flush=True) # Get the absolute path to the project root directory (two levels up from the current directory) project_root = os.path.abspath(os.path.join(current_dir, ".", "..")) print(f"Project root directory: {project_root}", flush=True) # Load environment variables from the .env file located in the project root directory dotenv_path = os.path.join(project_root, ".env") print(f"Loading environment variables from: {dotenv_path}", flush=True) load_dotenv(dotenv_path) # defining credentials file path from environment variable credentials_file_path = os.getenv("GOOGLE_API_CREDENTIALS") # defining scopes for drive and sheets API SCOPES = [ "https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/drive", ] # authenticate drive API def driveAPI(SCOPES): """ Authenticate with Google Drive API. Parameters: SCOPES (list): A list of OAuth 2.0 scopes defining the level of access to Google Drive resources. Returns: Resource: An authenticated Google Drive service object. Raises: HttpError: An error occurred while attempting to authenticate or build the service. """ credentials = None # Check if token file exists if os.path.exists("token.json"): credentials = Credentials.from_authorized_user_file("token.json", scopes=SCOPES) # If credentials are not valid or do not exist, perform authentication if not credentials or not credentials.valid: if credentials and credentials.expired and credentials.refresh_token: # Refresh the expired credentials credentials.refresh(Request()) else: # Perform OAuth 2.0 authentication flow = InstalledAppFlow.from_client_secrets_file( credentials_file_path, SCOPES ) credentials = flow.run_local_server(port=0) # Save the refreshed or newly acquired credentials to token file with open("token.json", "w") as token: token.write(credentials.to_json()) try: # Build the Google Drive service drive_service = build("drive", "v3", credentials=credentials) return drive_service except HttpError as error: # Handle any HTTP errors print(error) # Authenticate sheets API def sheetsAPI(SCOPES): """ Authenticate with Google Sheets API. Parameters: SCOPES (list): A list of OAuth 2.0 scopes defining the level of access to Google Sheets resources. Returns: Resource: An authenticated Google Sheets service object. Raises: HttpError: An error occurred while attempting to authenticate or build the service. """ credentials = None # Check if token file exists if os.path.exists("token.json"): credentials = Credentials.from_authorized_user_file("token.json", scopes=SCOPES) # If credentials are not valid or do not exist, perform authentication if not credentials or not credentials.valid: if credentials and credentials.expired and credentials.refresh_token: # Refresh the expired credentials credentials.refresh(Request()) else: # Perform OAuth 2.0 authentication flow = InstalledAppFlow.from_client_secrets_file( credentials_file_path, SCOPES ) credentials = flow.run_local_server(port=0) # Save the refreshed or newly acquired credentials to token file with open("token.json", "w") as token: token.write(credentials.to_json()) try: # Build the Google Sheets service sheets_service = build("sheets", "v4", credentials=credentials) return sheets_service except HttpError as error: # Handle any HTTP errors print(error)
已尝试上述代码,也在GCP控制台中查找过修改令牌过期时间的选项,但并未找到。
解决方案
针对固定单Google账号、Docker化应用的场景,最适合的方案是使用服务账号认证,彻底规避令牌过期和手动授权的问题。
步骤1:创建服务账号并获取密钥
- 登录GCP控制台进入目标项目
- 导航到「IAM与Admin」→「服务账号」
- 点击「创建服务账号」,完成名称和描述填写后创建
- 为服务账号添加对应角色:
- Drive API:添加「Drive文件管理员」或更细粒度的读写权限
- Sheets API:添加「Sheets编辑器」或匹配需求的权限
- 进入服务账号详情页,切换到「密钥」标签,点击「添加密钥」→「创建新密钥」,选择JSON格式下载密钥文件(命名为
service_account_key.json)
步骤2:修改认证代码
替换原有OAuth认证逻辑,改用服务账号认证,修改后的authenticate.py示例如下:
import os from dotenv import load_dotenv from google.oauth2.service_account import Credentials from googleapiclient.discovery import build from googleapiclient.errors import HttpError # 加载环境变量 current_dir = os.path.dirname(os.path.abspath(__file__)) project_root = os.path.abspath(os.path.join(current_dir, ".", "..")) dotenv_path = os.path.join(project_root, ".env") load_dotenv(dotenv_path) # 服务账号密钥路径 SERVICE_ACCOUNT_KEY_PATH = os.getenv("GOOGLE_SERVICE_ACCOUNT_KEY") # API权限范围 SCOPES = [ "https://www.googleapis.com/auth/spreadsheets", "https://www.googleapis.com/auth/drive", ] def get_credentials(): """获取服务账号凭证""" return Credentials.from_service_account_file( SERVICE_ACCOUNT_KEY_PATH, scopes=SCOPES ) # 认证Drive API def driveAPI(): try: credentials = get_credentials() drive_service = build("drive", "v3", credentials=credentials) return drive_service except HttpError as error: print(error) # 认证Sheets API def sheetsAPI(): try: credentials = get_credentials() sheets_service = build("sheets", "v4", credentials=credentials) return sheets_service except HttpError as error: print(error)
步骤3:共享资源给服务账号
服务账号是独立身份,需要将待操作的Drive文件夹、Sheets电子表格共享给服务账号的邮箱(格式为xxx@xxx.iam.gserviceaccount.com,可在服务账号详情页查看),并授予对应读写权限。
步骤4:Docker环境配置
- 在
.env中配置密钥路径:GOOGLE_SERVICE_ACCOUNT_KEY=./service_account_key.json - 通过Docker卷挂载
service_account_key.json到容器内(不建议打包进镜像,避免密钥泄露)
方案优势
- 服务账号凭证无过期限制,只要密钥安全保存即可长期使用
- 无需手动授权,容器启动后自动完成认证,适配Docker化部署
- 权限可控,通过GCP IAM和资源共享精确控制访问范围
额外注意事项
- 服务账号密钥文件需妥善保管,禁止提交到版本控制系统
- 可自定义IAM角色,仅授予应用所需的最小权限,提升安全性
- 若必须使用OAuth流程(如操作个人Drive资源),可先在本地完成授权获取带刷新令牌的
token.json,再通过Docker卷挂载到容器,原有代码的刷新逻辑会自动更新访问令牌,只要刷新令牌未被吊销即可长期使用
内容的提问来源于stack exchange,提问作者Srikar v
相关产品推荐
相关产品推荐

