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

Django集成Google Drive API部署至生产环境后出现(mismatching_state) CSRF状态不匹配错误求助

解决Django生产环境Google Drive上传的CSRF State不匹配问题

这个问题我之前也遇到过,根源在于你用错了OAuth2的授权流程——InstalledAppFlow.run_local_server()是给本地桌面应用设计的,它会启动一个本地临时服务器处理授权回调,完全不适合生产环境的Web应用场景,这才导致了CSRF state不匹配的错误。下面是具体的排查和解决步骤:

核心问题分析

InstalledAppFlow的本地授权流程会在你的机器上生成一个state值并启动本地服务等待回调,但生产环境中,用户的请求是发送到你的Web服务器,而Google的回调无法正确关联到这个本地生成的state,自然就出现了“state not equal in request and response”的错误。我们需要切换到Web应用专用的OAuth2授权流程。

解决方案步骤

1. 调整Google Cloud Console的OAuth2配置

首先登录Google Cloud Console,找到你的项目的OAuth2客户端ID设置:

  • 在“授权重定向URI”中添加你生产环境的回调URL,比如https://your-production-domain.com/drive/auth/callback(后续代码会用到这个地址)。

2. 新增Django视图处理授权请求与回调

我们需要两个视图:一个发起授权请求,一个处理Google的回调并保存凭据。

发起授权的视图

from django.shortcuts import redirect
from django.http import HttpResponse
from google_auth_oauthlib.flow import Flow
import os
import pickle

def drive_auth(request):
    # 建议用环境变量存储密钥文件路径,不要硬编码
    CLIENT_SECRET_FILE = os.getenv(
        "GOOGLE_CLIENT_SECRET_PATH",
        "/home/djuka/reusabletechnologies/project_app/reBankMini/reBankMiniApp/client_secret_156600557463-8e2qka5c4t646t7t4ksmbluo3aovv4q6.apps.googleusercontent.com.json"
    )
    SCOPES = ['https://www.googleapis.com/auth/drive']
    REDIRECT_URI = "https://your-production-domain.com/drive/auth/callback"  # 替换成你的生产回调地址

    flow = Flow.from_client_secrets_file(
        CLIENT_SECRET_FILE,
        scopes=SCOPES,
        redirect_uri=REDIRECT_URI
    )

    # 生成授权URL和state值,state用于CSRF验证
    authorization_url, state = flow.authorization_url(
        access_type='offline',  # 请求离线访问权限,以便刷新令牌
        include_granted_scopes='true'
    )

    # 将state存入session,回调时验证
    request.session['drive_auth_state'] = state
    return redirect(authorization_url)

处理授权回调的视图

def drive_auth_callback(request):
    CLIENT_SECRET_FILE = os.getenv(
        "GOOGLE_CLIENT_SECRET_PATH",
        "/home/djuka/reusabletechnologies/project_app/reBankMini/reBankMiniApp/client_secret_156600557463-8e2qka5c4t646t7t4ksmbluo3aovv4q6.apps.googleusercontent.com.json"
    )
    SCOPES = ['https://www.googleapis.com/auth/drive']
    REDIRECT_URI = "https://your-production-domain.com/drive/auth/callback"

    # 验证CSRF state
    session_state = request.session.get('drive_auth_state')
    request_state = request.GET.get('state')
    if not session_state or session_state != request_state:
        return HttpResponse('CSRF state mismatch', status=400)

    # 用state和回调code获取凭据
    flow = Flow.from_client_secrets_file(
        CLIENT_SECRET_FILE,
        scopes=SCOPES,
        state=session_state,
        redirect_uri=REDIRECT_URI
    )
    flow.fetch_token(code=request.GET.get('code'))

    # 保存凭据到pickle文件(生产环境建议改用数据库存储,适配多实例场景)
    cred = flow.credentials
    pickle_file = "/path/to/production-safe/storage/token_drive_v3.pickle"  # 确保路径有读写权限
    with open(pickle_file, 'wb') as token:
        pickle.dump(cred, token)

    return HttpResponse('Drive授权成功!可以开始上传文件了。')

3. 修改Create_Service函数适配生产环境

移除本地授权逻辑,只保留凭据加载和刷新功能:

import pickle
import os
from googleapiclient.discovery import build
from google.auth.transport.requests import Request

def Create_Service(client_secret_file, api_name, api_version, scopes):
    API_SERVICE_NAME = api_name
    API_VERSION = api_version
    SCOPES = scopes
    cred = None
    pickle_file = "/path/to/production-safe/storage/token_drive_v3.pickle"  # 和回调视图中的路径一致

    # 加载已保存的凭据
    if os.path.exists(pickle_file):
        with open(pickle_file, 'rb') as token:
            cred = pickle.load(token)

    # 刷新过期的凭据
    if cred and cred.expired and cred.refresh_token:
        cred.refresh(Request())
        with open(pickle_file, 'wb') as token:
            pickle.dump(cred, token)

    try:
        service = build(API_SERVICE_NAME, API_VERSION, credentials=cred)
        print(f"{API_SERVICE_NAME} service created successfully")
        return service
    except Exception as e:
        print('Unable to connect.')
        print(e)
        return None

4. 调整send_drive函数(仅需少量修改)

def send_drive(file_name, file_path):
    CLIENT_SECRET_FILE = os.getenv(
        "GOOGLE_CLIENT_SECRET_PATH",
        "/home/djuka/reusabletechnologies/project_app/reBankMini/reBankMiniApp/client_secret_156600557463-8e2qka5c4t646t7t4ksmbluo3aovv4q6.apps.googleusercontent.com.json"
    )
    API_NAME = 'drive'
    API_VERSION = 'v3'
    SCOPES = ['https://www.googleapis.com/auth/drive']
    service = Create_Service(CLIENT_SECRET_FILE, API_NAME, API_VERSION, SCOPES)
    
    if not service:
        return None  # 处理服务创建失败的情况

    # 上传文件逻辑不变
    file_metadata = {
        'name': file_name,
        'parents': ['1WpY7cw3S5RAPvCfFyPMDkw0I3vIZfQ_c']
    }
    media_content = MediaFileUpload(file_path, mimetype='application/pdf')
    file = service.files().create(
        body=file_metadata,
        media_body=media_content
    ).execute()
    print(file)
    return file

5. 生产环境初始化

部署完成后,先访问/drive/auth(对应你配置的URL)完成一次授权流程,生成token_drive_v3.pickle文件,之后send_drive函数就能正常工作了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 14:17:36