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

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:创建服务账号并获取密钥

  1. 登录GCP控制台进入目标项目
  2. 导航到「IAM与Admin」→「服务账号」
  3. 点击「创建服务账号」,完成名称和描述填写后创建
  4. 为服务账号添加对应角色:
    • Drive API:添加「Drive文件管理员」或更细粒度的读写权限
    • Sheets API:添加「Sheets编辑器」或匹配需求的权限
  5. 进入服务账号详情页,切换到「密钥」标签,点击「添加密钥」→「创建新密钥」,选择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环境配置

  1. 在.env中配置密钥路径:GOOGLE_SERVICE_ACCOUNT_KEY=./service_account_key.json
  2. 通过Docker卷挂载service_account_key.json到容器内(不建议打包进镜像,避免密钥泄露)

方案优势

  • 服务账号凭证无过期限制,只要密钥安全保存即可长期使用
  • 无需手动授权,容器启动后自动完成认证,适配Docker化部署
  • 权限可控,通过GCP IAM和资源共享精确控制访问范围

额外注意事项

  • 服务账号密钥文件需妥善保管,禁止提交到版本控制系统
  • 可自定义IAM角色,仅授予应用所需的最小权限,提升安全性
  • 若必须使用OAuth流程(如操作个人Drive资源),可先在本地完成授权获取带刷新令牌的token.json,再通过Docker卷挂载到容器,原有代码的刷新逻辑会自动更新访问令牌,只要刷新令牌未被吊销即可长期使用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 05:01:00