【问题标题】:Getting a Error 400: redirect_uri_mismatch when trying to use OAuth2 with Google Sheets from a Django view尝试从 Django 视图将 OAuth2 与 Google 表格一起使用时出现错误 400:redirect_uri_mismatch
【发布时间】:2020-05-30 02:50:05
【问题描述】:

我正在尝试从 Django 视图连接到 Google Sheets 的 API。我从这个链接中获取的大部分代码: https://developers.google.com/sheets/api/quickstart/python

不管怎样,代码如下:

sheets.py(从上面的链接复制粘贴,函数重命名)

from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/spreadsheets.readonly']

# The ID and range of a sample spreadsheet.
SAMPLE_SPREADSHEET_ID = '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms'
SAMPLE_RANGE_NAME = 'Class Data!A2:E'

def test():
    """Shows basic usage of the Sheets API.
    Prints values from a sample spreadsheet.
    """
    creds = None
    # The file token.pickle stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if os.path.exists('token.pickle'):
        with open('token.pickle', 'rb') as token:
            creds = pickle.load(token)
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                'credentials.json', SCOPES)
            creds = flow.run_local_server(port=0)
        # Save the credentials for the next run
        with open('token.pickle', 'wb') as token:
            pickle.dump(creds, token)

    service = build('sheets', 'v4', credentials=creds)

    # Call the Sheets API
    sheet = service.spreadsheets()
    result = sheet.values().get(spreadsheetId=SAMPLE_SPREADSHEET_ID,
                                range=SAMPLE_RANGE_NAME).execute()
    values = result.get('values', [])

    if not values:
        print('No data found.')
    else:
        print('Name, Major:')
        for row in values:
            # Print columns A and E, which correspond to indices 0 and 4.
            print('%s, %s' % (row[0], row[4]))

urls.py

urlpatterns = [
    path('', views.index, name='index')
]

views.py

from django.http import HttpResponse
from django.shortcuts import render

from .sheets import test

# Views

def index(request):
    test()
    return HttpResponse('Hello world')

视图函数所做的只是调用 sheets.py 模块中的test() 方法。无论如何,当我运行我的服务器并访问 URL 时,会为 Google oAuth2 打开另一个选项卡,这意味着检测到凭据文件和所有内容。但是,在此选项卡中,Google 会显示以下错误消息:

Error 400: redirect_uri_mismatch The redirect URI in the request, http://localhost:65262/, does not match the ones authorized for the OAuth client.

在我的 API 控制台中,我将回调 URL 完全设置为 127.0.0.1:8000 以匹配我的 Django 的视图 URL。我什至不知道http://localhost:65262/ URL 来自哪里。解决这个问题有什么帮助吗?有人可以向我解释为什么会这样吗?提前致谢。

编辑 我尝试删除注释中提到的流方法中的port=0,然后与http://localhost:8080/ 发生URL 不匹配,这再次很奇怪,因为我的Django 应用程序在8000 端口中运行。

【问题讨论】:

  • 虽然我不确定这是否是直接的解决方案,但当creds = flow.run_local_server(port=0)port=0creds = flow.run_local_server() 一样被删除时,你会得到什么结果?
  • @Tanaike 您使用的flow 方法是什么?
  • 感谢您的回复。对于不完整的评论,我深表歉意。而且,我不得不道歉,我的评论没有解决你的问题。关于google_auth_oauthlib.flow模块,文档见here
  • 您可以在credentials.json 文件中看到哪些重定向 URI?
  • @RafaGuillermo 我有这个:http://127.0.0.1:8000/

标签: python django google-api google-oauth google-sheets-api


【解决方案1】:

您不应该使用Flow.run_local_server(),除非您不打算部署代码。这是因为run_local_server 在服务器上启动了一个浏览器来完成流程。

如果您是为自己在本地开发项目,这很好用。

如果您打算使用本地服务器来协商 OAuth 流程。您的机密中配置的重定向 URI 必须匹配,主机的本地服务器默认值为 localhost and port is 8080

如果您要部署代码,则必须通过用户浏览器、您的服务器和 Google 之间的交换来执行流程。

由于您已经有一个 Django 服务器正在运行,您可以使用它来协商流程。

例如,

假设在一个带有urls.py模块的Django项目中有一个推文应用程序如下。

from django.urls import path, include

from . import views

urlpatterns = [
    path('google_oauth', views.google_oath, name='google_oauth'),
    path('hello', views.say_hello, name='hello'),
]

urls = include(urlpatterns)

您可以为需要以下凭据的视图实施保护。

import functools
import json
import urllib

from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request

from django.shortcuts import redirect
from django.http import HttpResponse

SCOPES = ['https://www.googleapis.com/auth/userinfo.email', 'https://www.googleapis.com/auth/userinfo.profile', 'openid']

def provides_credentials(func):
    @functools.wraps(func)
    def wraps(request):
        # If OAuth redirect response, get credentials
        flow = InstalledAppFlow.from_client_secrets_file(
            'credentials.json', SCOPES,
            redirect_uri="http://localhost:8000/tweet/hello")

        existing_state = request.GET.get('state', None)
        current_path = request.path
        if existing_state:
            secure_uri = request.build_absolute_uri(
                ).replace('http', 'https')
            location_path = urllib.parse.urlparse(existing_state).path 
            flow.fetch_token(
                authorization_response=secure_uri,
                state=existing_state
            )
            request.session['credentials'] = flow.credentials.to_json()
            if location_path == current_path:
                return func(request, flow.credentials)
            # Head back to location stored in state when
            # it is different from the configured redirect uri
            return redirect(existing_state)


        # Otherwise, retrieve credential from request session.
        stored_credentials = request.session.get('credentials', None)
        if not stored_credentials:
            # It's strongly recommended to encrypt state.
            # location is needed in state to remember it.
            location = request.build_absolute_uri() 
            # Commence OAuth dance.
            auth_url, _ = flow.authorization_url(state=location)
            return redirect(auth_url)

        # Hydrate stored credentials.
        credentials = Credentials(**json.loads(stored_credentials))

        # If credential is expired, refresh it.
        if credentials.expired and creds.refresh_token:
            creds.refresh(Request())

        # Store JSON representation of credentials in session.
        request.session['credentials'] = credentials.to_json()

        return func(request, credentials=credentials)
    return wraps


@provides_credentials
def google_oauth(request, credentials):
    return HttpResponse('Google OAUTH <a href="/tweet/hello">Say Hello</a>')

@provides_credentials
def say_hello(request, credentials):
    # Use credentials for whatever
    return HttpResponse('Hello')

请注意,这只是一个示例。如果您决定走这条路,我建议您考虑将 OAuth 流提取到它自己的 Django 应用程序中。

【讨论】:

    【解决方案2】:

    我在 redirect_uri 错误中遇到了同样的问题,结果证明(如上所述)我在 google 控制台中创建了我的凭据,类型为“Web 服务器”而不是“桌面应用程序”。我创建了新的凭据作为“桌面应用程序”,下载了 JSON 并且它工作了。

    最终,我想将 GMAIL API 用于 Web 服务器,但这是与示例不同的流程。

    【讨论】:

      【解决方案3】:

      重定向 URI 告诉 Google 您希望将授权返回到的位置。这必须在谷歌开发者控制台中正确设置,以避免任何人劫持您的客户端。它必须完全匹配。

      Google developer console。编辑您当前使用的客户端并将以下内容添加为重定向 uri

      http://localhost:65262/
      

      提示点击小铅笔图标来编辑客户端:)

      TBH 在开发过程中更容易添加 google 说你正在调用的端口,然后在你的应用程序中摆弄设置。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-02
        • 1970-01-01
        • 2021-09-06
        • 1970-01-01
        • 2018-02-22
        • 1970-01-01
        相关资源
        最近更新 更多