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

Django应用获取自有Google Calendar读写权限遇认证问题

搞定Django中Google Calendar读写权限的认证问题

嗨,看你折腾了服务账号、客户端密钥都踩坑,API Explorer的弹窗授权又不符合后台需求,咱们就顺着你推测的Web Server Application场景,一步步把问题解决掉,同时也给你排查下服务账号的常见坑。

先说说服务账号方式为啥失败(针对Google Workspace用户)

如果你用的是企业Google Workspace账号,服务账号其实是可行的,但你大概率没做域范围委派——这是服务账号访问用户日历的关键步骤:

  • 登录Google Cloud控制台,找到你的服务账号,进入「权限」标签,点击「创建委派」
  • 输入Calendar API的读写范围:https://www.googleapis.com/auth/calendar
  • 接着登录Google Workspace管理员后台,在「安全」→「API控制」→「域名范围委派」里,添加服务账号的客户端ID,把刚才的API范围填进去
  • 代码里必须指定要模拟的用户邮箱(就是你要访问的日历所属账号),示例代码如下:
    from google.oauth2 import service_account
    
    SCOPES = ['https://www.googleapis.com/auth/calendar']
    SERVICE_ACCOUNT_FILE = '你的服务账号密钥文件路径.json'
    
    credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES, subject='你的日历所有者邮箱@xxx.com')
    
    没加subject参数的话,服务账号根本没权限访问具体用户的日历,这是很多人踩的坑!

个人账号/非Workspace场景:用Web Server离线授权实现无弹窗后台访问

如果是个人Google账号,服务账号没法直接用,那Web Server的授权流程可以实现一次性授权后长期用刷新令牌,彻底摆脱弹窗:

1. 先在Google Cloud配置好客户端ID

  • 进入Google Cloud控制台,创建OAuth 2.0客户端ID,选择「Web应用」类型
  • 填写「已授权的重定向URI」,比如https://你的Django域名.com/google-calendar/callback(要和后面Django的视图路由对应上)
  • 下载客户端密钥JSON文件,放到Django项目的安全目录里(别提交到代码仓库)

2. Django里实现授权和回调逻辑

(1)写个视图生成授权跳转链接

from django.shortcuts import redirect
from google_auth_oauthlib.flow import Flow

SCOPES = ['https://www.googleapis.com/auth/calendar']
CLIENT_SECRETS_FILE = '你的客户端密钥文件路径.json'

def google_calendar_auth(request):
    flow = Flow.from_client_secrets_file(
        CLIENT_SECRETS_FILE,
        scopes=SCOPES,
        redirect_uri='https://你的Django域名.com/google-calendar/callback')
    
    # 必须设置access_type为offline,才能拿到刷新令牌
    authorization_url, state = flow.authorization_url(
        access_type='offline',
        include_granted_scopes='true')
    
    # 把state存到session里,回调时用来验证请求合法性
    request.session['state'] = state
    return redirect(authorization_url)

(2)回调视图获取并保存令牌

from django.http import HttpResponse
from google_auth_oauthlib.flow import Flow
import json

def google_calendar_callback(request):
    state = request.session.get('state')
    flow = Flow.from_client_secrets_file(
        CLIENT_SECRETS_FILE,
        scopes=SCOPES,
        state=state,
        redirect_uri='https://你的Django域名.com/google-calendar/callback')
    
    # 从回调请求里获取令牌
    flow.fetch_token(authorization_response=request.build_absolute_uri())
    
    # 拿到包含刷新令牌的凭证,一定要加密存储(示例用文件,实际推荐存数据库并加密)
    credentials = flow.credentials
    creds_data = {
        'token': credentials.token,
        'refresh_token': credentials.refresh_token,
        'token_uri': credentials.token_uri,
        'client_id': credentials.client_id,
        'client_secret': credentials.client_secret,
        'scopes': credentials.scopes
    }
    
    with open('encrypted_credentials.json', 'w') as f:
        json.dump(creds_data, f)
    
    return HttpResponse("授权搞定!现在后台就能访问Google Calendar了")

(3)用保存的令牌访问Calendar API

from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
import json
from google.auth.transport.requests import Request

def access_calendar():
    # 读取保存的凭证
    with open('encrypted_credentials.json', 'r') as f:
        creds_data = json.load(f)
    
    credentials = Credentials.from_authorized_user_info(creds_data)
    
    # 令牌过期自动刷新,不用手动管
    if credentials.expired and credentials.refresh_token:
        credentials.refresh(Request())
    
    # 构建Calendar服务实例
    service = build('calendar', 'v3', credentials=credentials)
    
    # 测试下获取日历列表
    calendars = service.calendarList().list().execute()
    print(calendars)

几个关键提醒

  • 个人账号只能用Web Server离线授权方式,服务账号对个人账号无效
  • 刷新令牌一定要加密存储,绝对不能明文放在代码或配置里
  • 本地测试可以用http://localhost:8000作为重定向URI,但正式环境必须用HTTPS
  • API Explorer的弹窗是交互式授权,只适合测试,后台应用必须靠离线授权拿到refresh_token才能长期运行

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:13:58