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

Django生产环境BigQuery Google Auth无法跳转及回调异常问题

Django部署BigQuery OAuth授权问题解决

问题详情

本地运行Django调用BigQuery查询一切正常,但部署到生产服务器后遇到两个问题:

  • 无法自动跳转到Google验证链接
  • 手动打开验证链接完成授权后,无法正常回调到应用
  • 修改appflow的redirect_uri参数后,生成的授权链接依然用localhost:8080作为回调地址
  • 已在Google Cloud控制台添加正确的授权重定向URI,并将API设置为生产模式
  • 生成的授权链接示例:
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=123-nml31ekr2n0didomei5.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fbigquery&state=XF1WdtCoR4HaICwzSKk9A1giBrSzBv&access_type=offline
  • 相关代码片段:
def query_stackoverflow():
    launch_browser = True
    project = 'xx-prod'
    appflow = flow.InstalledAppFlow.from_client_secrets_file(
        "static/client_secret_518684-nmpoqtgo5flvcgnl31ekr2ni5.apps.googleusercontent.com.json", 
        scopes=["https://www.googleapis.com/auth/bigquery"], 
        redirect_uri=["https://xx.com/"]
    )

    if launch_browser:
        appflow.run_local_server()
    else:
        appflow.run_console()
    credentials = appflow.credentials
    client = bigquery.Client(project=project, credentials=credentials)
    client = bigquery.Client()
    query_job = client.query(
        """
        SELECT
          CONCAT(
            'https://stackoverflow.com/questions/',
            CAST(id as STRING)) as url,
          view_count
        FROM `bigquery-public-data.stackoverflow.posts_questions`
        WHERE tags like '%google-bigquery%'
        ORDER BY view_count DESC
        LIMIT 10"""
    )

    results = query_job.result()  # Waits for job to complete.

    for row in results:
        print("{} : {} views".format(row.url, row.view_count))

核心问题

你用错了授权流程类:InstalledAppFlow是专门给本地桌面应用设计的,它的run_local_server()方法会强制覆盖你设置的redirect_uri,默认用localhost:8080。生产服务器上的Web应用必须用WebAppFlow类来适配标准的OAuth 2.0 Web应用流程。

解决步骤

1. 替换为WebAppFlow并重写授权逻辑

修改代码,拆分授权发起、回调处理和查询执行三个部分:

from google_auth_oauthlib.flow import Flow
from google.oauth2.credentials import Credentials
from django.http import HttpResponseRedirect, HttpResponse
from django.shortcuts import render
from django.conf import settings
import os
import bigquery

# 初始化授权Flow
def get_auth_flow(request):
    return Flow.from_client_secrets_file(
        os.path.join(settings.STATIC_ROOT, "client_secret_518684-nmpoqtgo5flvcgnl31ekr2ni5.apps.googleusercontent.com.json"),
        scopes=["https://www.googleapis.com/auth/bigquery"],
        redirect_uri="https://xx.com/oauth2callback/"  # 必须和Google Cloud控制台配置完全一致
    )

# 发起授权请求
def oauth_authorize(request):
    flow = get_auth_flow(request)
    # 生成授权链接,请求离线访问权限(获取refresh_token)
    auth_url, state = flow.authorization_url(
        access_type='offline',
        include_granted_scopes='true'
    )
    # 保存state到session,回调时验证防止CSRF攻击
    request.session['oauth_state'] = state
    return HttpResponseRedirect(auth_url)

# 处理授权回调
def oauth2callback(request):
    saved_state = request.session.get('oauth_state')
    current_state = request.GET.get('state')
    
    if not saved_state or saved_state != current_state:
        return HttpResponse('无效的state参数', status=400)
    
    flow = get_auth_flow(request)
    # 用回调地址的完整URL获取token
    flow.fetch_token(authorization_response=request.build_absolute_uri())
    
    # 将凭证信息保存到session(生产环境建议加密后存数据库)
    credentials = flow.credentials
    request.session['bigquery_credentials'] = {
        '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
    }
    
    # 回调完成后跳转到查询页面
    return HttpResponseRedirect('/query_stackoverflow/')

# 执行BigQuery查询
def query_stackoverflow(request):
    # 检查是否已有授权凭证
    creds_info = request.session.get('bigquery_credentials')
    if not creds_info:
        # 没有凭证就跳转到授权页面
        return HttpResponseRedirect('/oauth_authorize/')
    
    # 从session还原凭证
    credentials = Credentials(**creds_info)
    project = 'xx-prod'
    client = bigquery.Client(project=project, credentials=credentials)
    
    query_job = client.query(
        """
        SELECT
          CONCAT(
            'https://stackoverflow.com/questions/',
            CAST(id as STRING)) as url,
          view_count
        FROM `bigquery-public-data.stackoverflow.posts_questions`
        WHERE tags like '%google-bigquery%'
        ORDER BY view_count DESC
        LIMIT 10"""
    )
    
    results = query_job.result()
    # 将结果传递给模板渲染(替换成你的模板路径)
    return render(request, 'query_results.html', {'results': results})

2. 配置Django路由

在项目的urls.py中添加三个路由:

from django.urls import path
from . import views

urlpatterns = [
    path('oauth_authorize/', views.oauth_authorize, name='oauth_authorize'),
    path('oauth2callback/', views.oauth2callback, name='oauth2callback'),
    path('query_stackoverflow/', views.query_stackoverflow, name='query_stackoverflow'),
]

3. 验证Google Cloud控制台配置

  • 确保OAuth客户端类型是Web应用(不要选桌面应用)
  • 授权重定向URI列表中已添加https://xx.com/oauth2callback/(注意末尾的斜杠要和代码中一致)

4. 解决自动跳转问题

生产服务器上无法自动打开浏览器是正常的,Web应用的授权逻辑是引导用户访问/oauth_authorize/路由,直接重定向到Google的授权页面,而非依赖run_local_server()这种本地桌面应用的行为。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 03:40:32