python - 在 google api + Django 中使用 State 变量的问题
问题描述
我正在尝试将文件保存到 google 磁盘的功能添加到 django 应用程序。我以烧瓶链接中编写的官方文档中的示例为基础
import google_auth_oauthlib.flow
from googleapiclient.discovery import build
from django.urls import reverse
from django.http import HttpResponse, HttpResponseRedirect
import os
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1'
SCOPES = 'https://www.googleapis.com/auth/drive'
cred = os.path.join(settings.BASE_DIR, 'credentials.json')
上传文件功能:
def file_to_drive(request, import_file=None):
state = request.session['state']
if state is None:
authorize(request)
else:
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
cred, scopes=SCOPES, state=state)
flow.redirect_uri = "http://localhost:8000/oauth2callback"
authorization_response = request.build_absolute_uri()
flow.fetch_token(authorization_response=authorization_response)
credentials = flow.credentials
service = build('drive', 'v3', credentials=credentials)
file_metadata = {
'name': 'My Report',
'mimeType': 'application/vnd.google-apps.spreadsheet'
}
media = MediaFileUpload(import_file,
mimetype='text/html',
resumable=True)
file = service.files().create(body=file_metadata,
media_body=media,
fields='id').execute()
print('File ID: %s' % file.get('id'))
return (f"https://docs.google.com/document/d/{file.get('id')}/edit")
以及用户授权功能
def authorize(request):
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
cred, scopes=SCOPES)
flow.redirect_uri = "http://localhost:8000/oauth2callback"
authorization_url, state = flow.authorization_url(
access_type='offline',
include_granted_scopes='true')
request.session['state'] = state
return HttpResponseRedirect(authorization_url)
网址.py
path('oauth2callback', authorize, name='authorize'),
path('to_drive', file_to_drive, name='file_to_drive'),
在函数file_to_drive
中从会话中搜索状态参数的值,如果没有找到,authorize
则调用该函数。最后,我收到一条消息
oauthlib.oauth2.rfc6749.errors.MismatchingStateError: (mismatching_state) CSRF Warning! State not equal in request and response.
错误回溯看起来像
File "/home/y700/projects/CV/cv-base/ems/base/utils.py", line 119, in file_to_drive
flow.fetch_token(authorization_response=authorization_response)
File "/home/y700/Env/cv-base-XRtgVf2K/lib/python3.7/site-packages/google_auth_oauthlib/flow.py", line 263, in fetch_token
self.client_config['token_uri'], **kwargs)
File "/home/y700/Env/cv-base-XRtgVf2K/lib/python3.7/site-packages/requests_oauthlib/oauth2_session.py", line 208, in fetch_token
state=self._state)
File "/home/y700/Env/cv-base-XRtgVf2K/lib/python3.7/site-packages/oauthlib/oauth2/rfc6749/clients/web_application.py", line 203, in parse_request_uri_response
response = parse_authorization_code_response(uri, state=state)
File "/home/y700/Env/cv-base-XRtgVf2K/lib/python3.7/site-packages/oauthlib/oauth2/rfc6749/parameters.py", line 268, in parse_authorization_code_response
raise MismatchingStateError()
oauthlib.oauth2.rfc6749.errors.MismatchingStateError: (mismatching_state) CSRF Warning! State not equal in request and response.
[13/Sep/2019 13:19:52] "GET /to_drive HTTP/1.1" 500 98701
错误是由字符串引起的
flow.fetch_token(authorization_response=authorization_response)
解决方案
错误在这行代码中:
authorization_response = request.build_absolute_uri()
它确实适用于烧瓶,但给 Django 带来了麻烦。问题在于 URL 的编码。我尝试了不同的编码器,但没有奏效。所以我使用字符串操作对代码进行了编码。
#encoding link
link = request.build_absolute_uri()
link = str(link)
s = link.index("code=")
cut0 = link[0:s+2]
cut1 = link[s+2:len(link)]
cut1 = cut1.replace("/","%2F")
final = cut0+cut1
authorization_response = final
flow.fetch_token(authorization_response=authorization_response)
credentials = flow.credentials
# Store credentials
# request.session['credentials'] = credentials_to_dict(credentials)
# print(request.session['credentials'])
credentials_dict = credentials_to_dict(credentials)
# print(credentials_dict)
credentials = google.oauth2.credentials.Credentials(
credentials_dict["token"],
refresh_token = credentials_dict["refresh_token"],
token_uri = credentials_dict["token_uri"],
client_id = credentials_dict["client_id"],
client_secret = credentials_dict["client_secret"],
scopes = credentials_dict["scopes"])
service = build('drive', 'v3', credentials=credentials)
file_metadata = { ......
将凭据存储在 DB 而不是 Session 中。
推荐阅读
- spring-boot - 有人可以为我的 Azure DevOps 部署建议一个好的做法吗
- sap-cloud-sdk - OData PATCH 请求导致 IllegalArgumentException
- python - 优化python DFS(for循环效率低下)
- c# - 如何从 IChangeSet 中提取单个项目?
- excel - 针对另一个范围过滤的范围的 Excel [标准偏差]
- java - 使用 SpringBoot 的 MongoDb 事务
- java - 如何使用 JavaParser 在同一行添加新语句
- ruby-on-rails - 如何使用 Rails Capybara 检查行为类似于按钮的禁用 div?
- python - M. Zelle's Graphics 的圆弧运动
- javascript - javascript中的动态变化,不会移动我的div