我正在尝试创建一个 Flask 应用,该应用可以浏览到用户的 Google 云端硬盘以选择要转换为 CSV 格式的文件。
我在网上找到了添加 Google 登录的代码(见下文),但我不明白如何打印登录用户的所有文件。我发现我需要将 https://www.googleapis.com/auth/drive.file
添加到 scope
但我不明白如何列出所有文件来自 index
中的授权用户 (current_user.paying==True
)。我知道可以使用以下代码来完成此操作,但我不确定如何定义凭据
。
在回调
中,已经生成了访问 token ,因此我尝试访问 Google Drive API 以仅搜索用户的 Google 表格:
from httplib2 import Http
from apiclient import discovery
from oauth2client import file
with open("./credentials.json", 'w') as outfile:
json.dump(token_response.json(), outfile)
store = file.Storage("./credentials.json")
credentials = store.get()
drive = discovery.build("drive", "v3", http=credentials.authorize(Http()))
files = drive.files().list(q="mimeType='application/vnd.google-apps.spreadsheet'").execute()
但这给了我一个“KeyError: '_module'”错误,信息量不大。
这是使用 Google 登录的完整 Flask 代码
import os
import requests
import json
from httplib2 import Http
from apiclient import discovery
from oauthlib.oauth2 import WebApplicationClient
from flask import Flask, redirect, request, url_for
from flask_login import LoginManager, current_user, login_required, login_user, logout_user
from flask_login import UserMixin
authorized_users = ["myuser@gmail.com"]
class User(UserMixin):
def __init__(self, id_, paying):
self.id = id_
self.paying = paying
@staticmethod
def get(user_email):
if user_email not in authorized_users:
user = User(user_email, False)
else:
user = User(user_email, True)
return user
# Configuration
GOOGLE_CLIENT_ID = os.environ.get("GOOGLE_CLIENT_ID", None)
GOOGLE_CLIENT_SECRET = os.environ.get("GOOGLE_CLIENT_SECRET", None)
GOOGLE_DISCOVERY_URL = ("https://accounts.google.com/.well-known/openid-configuration")
# Flask app setup
app = Flask(__name__)
app.secret_key = os.environ.get("SECRET_KEY") or os.urandom(24)
# User session management setup
login_manager = LoginManager()
login_manager.init_app(app)
# OAuth 2 client setup
client = WebApplicationClient(GOOGLE_CLIENT_ID)
# Flask-Login helper to retrieve a user from our db
@login_manager.user_loader
def load_user(user_email):
return User.get(user_email)
def get_google_provider_cfg():
return requests.get(GOOGLE_DISCOVERY_URL).json()
google_provider_cfg = get_google_provider_cfg()
token_endpoint = google_provider_cfg["token_endpoint"]
print("Google provider cfg", google_provider_cfg)
ACCESS_TOKEN_URI = 'https://www.googleapis.com/oauth2/v4/token'
@app.route("/")
def index():
if current_user.is_authenticated:
if current_user.paying:
return f"<p>Hello, {current_user.id}! You're logged in!</p><br/><a class='button' href='/logout'>Logout</a>"
else:
return "Not authorized!<br/><a class='button' href='/logout'>Logout</a>"
else:
return f"<a class='button' href='/login'>Google Login</a>"
@app.route("/login")
def login():
# Find out what URL to hit for Google login
google_provider_cfg = get_google_provider_cfg()
authorization_endpoint = google_provider_cfg["authorization_endpoint"]
# Use library to construct the request for Google login and provide
# scopes that let you retrieve user's profile from Google
request_uri = client.prepare_request_uri(
authorization_endpoint,
redirect_uri=request.base_url + "/callback",
scope=["openid", "email", "profile", "https://www.googleapis.com/auth/drive.file"],
)
return redirect(request_uri)
@app.route("/login/callback")
def callback():
# Get authorization code Google sent back to you
code = request.args.get("code")
# Prepare and send a request to get tokens! Yay tokens!
token_url, headers, body = client.prepare_token_request(
token_endpoint,
authorization_response=request.url,
redirect_url=request.base_url,
code=code
)
token_response = requests.post(
token_url,
headers=headers,
data=body,
auth=(GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET),
)
# Parse the tokens!
client.parse_request_body_response(json.dumps(token_response.json()))
# Now that you have tokens (yay) let's find and hit the URL
# from Google that gives you the user's profile information,
# including their Google profile image and email
userinfo_endpoint = google_provider_cfg["userinfo_endpoint"]
uri, headers, body = client.add_token(userinfo_endpoint)
userinfo_response = requests.get(uri, headers=headers, data=body)
if userinfo_response.json().get("email_verified"):
# unique_id = userinfo_response.json()["sub"]
users_email = userinfo_response.json()["email"]
picture = userinfo_response.json()["picture"]
users_name = userinfo_response.json()["given_name"]
else:
return "User email not available or not verified by Google.", 400
# Create a user in your db with the information provided
# by Google
user = User(users_email, False)
#
# # Doesn't exist? Add it to the database.
# if not User.get(unique_id):
# User.create(unique_id, users_name, users_email, picture)
print("Logging", users_name, users_email, picture)
# Begin user session by logging the user in
login_user(user)
# Send user back to homepage
return redirect(url_for("index"))
@app.route("/logout")
@login_required
def logout():
logout_user()
print("Logging out")
return redirect(url_for("index"))
if __name__ == "__main__":
app.run(ssl_context="adhoc", debug=True)
最佳答案
获取凭据的简单方法是
- 访问Google Drive API Quickstart
- 点击
启用驱动器
- 记下
客户端 ID
和客户端 key
,或者点击下载客户端配置
并打开生成和下载的凭据。 json
文件
或者,您可以
- 访问Google Cloud Console
- 选择
API 和服务
-> 凭据 点击+CREATE CREDENTIALS
更新
KeyError: '_module'
是由于检索结构不正确的 json
文件而产生的错误。
可能的原因:
- 使用需要 client_secrets.json 的 API 客户端库文件 - 这与您从 Google Drive API 快速入门获取的
credentials.json
文件不同 - 在
json
文件中存储无效凭据 - 将
json
文件存储在 py 文件之外的另一个文件夹中 - 使用无效范围
据我所知,您使用 WebApplicationClient并需要创建各自的client credentials for a Web Server .
json 文件
的内容应为
{"web":{"client_id":"XXXX.apps.googleusercontent.com","project_id":"XXXX","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"XXX","redirect_uris":["https://script.google.com/oauthcallback"]}}
另外:
范围https://www.googleapis.com/auth/drive.file不足以列出用户驱动器上的所有文件 - 它只允许您访问使用您的应用程序创建/打开的文件。更多信息here 。
出于调试目的,我建议您将您的应用程序放在第一个慷慨的范围内。一旦解决了凭据问题并且您的应用程序可以正常运行 - 看看您可以在不影响应用程序功能的情况下限制范围到什么程度。
关于具有 Google 云端硬盘访问权限的 Flask Google 登录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60852287/