Google 选择器身份验证弹出窗口被阻止
Posted
技术标签:
【中文标题】Google 选择器身份验证弹出窗口被阻止【英文标题】:Google picker auth popup is being blocked 【发布时间】:2015-07-19 11:40:08 【问题描述】:我有一个列出工作的移动网站,用户申请并上传他们的简历(简历) - 我希望他们能够从他们的 Google Drive 中选择一个文件。
我在这里创建了 Hello world 示例 - https://developers.google.com/picker/docs/(为方便起见,此处复制代码)
问题是,如果尚未登录云端硬盘,则会启动一个登录弹出窗口。这在台式机上已经够糟糕了,但在手机上真的很糟糕。
我试过this solution,但得到'TypeError: gapi.auth is undefined'
我还尝试从 onclick 事件而不是文档中描述的 onload 启动选择器。
function launchDrive()
gapi.load('auth', 'callback': onAuthApiLoad);
gapi.load('picker', 'callback': onPickerApiLoad);
<input type='button' value='Launch Drive' onclick='launchDrive();'>
Google 代码示例:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google Picker Example</title>
<script type="text/javascript">
var developerKey = 'xxxxxxxYYYYYYYY-12345678';
var clientId = "1234567890-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com"
var scope = ['https://www.googleapis.com/auth/photos'];
var pickerApiLoaded = false;
var oauthToken;
function onApiLoad()
gapi.load('auth', 'callback': onAuthApiLoad);
gapi.load('picker', 'callback': onPickerApiLoad);
function onAuthApiLoad()
window.gapi.auth.authorize(
'client_id': clientId,
'scope': scope,
'immediate': false
,
handleAuthResult);
function onPickerApiLoad()
pickerApiLoaded = true;
createPicker();
function handleAuthResult(authResult)
if (authResult && !authResult.error)
oauthToken = authResult.access_token;
createPicker();
// Create and render a Picker object for picking user Photos.
function createPicker()
if (pickerApiLoaded && oauthToken)
var picker = new google.picker.PickerBuilder().
addView(google.picker.ViewId.PHOTOS).
setOAuthToken(oauthToken).
setDeveloperKey(developerKey).
setCallback(pickerCallback).
build();
picker.setVisible(true);
// A simple callback implementation.
function pickerCallback(data)
var url = 'nothing';
if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED)
var doc = data[google.picker.Response.DOCUMENTS][0];
url = doc[google.picker.Document.URL];
var message = 'You picked: ' + url;
document.getElementById('result').innerHTML = message;
</script>
</head>
<body>
<div id="result"></div>
<!-- The Google API Loader script. -->
<script type="text/javascript" src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
</body>
</html>
2015 年 5 月 13 日编辑
除了杰森的回答,这也是我尝试过的(由 oncllick 按钮调用):
function launchDrive()
//gapi.load('auth', 'callback': onAuthApiLoad);
gapi.auth.init(onAuthApiLoad);
gapi.load('picker', 'callback': onPickerApiLoad);
【问题讨论】:
【参考方案1】:您将需要调用 gapi.auth.init。在此处查看文档:https://developers.google.com/api-client-library/javascript/reference/referencedocs#gapiauthinit
初始化授权功能。在客户端加载时调用它,以防止弹出窗口阻止程序在 gapi.auth.authorize 调用上阻止身份验证窗口。
【讨论】:
杰森,感谢并为我迟到的回复道歉。正如我在问题中提到的那样,我已经尝试过了——我已经尝试过这个解决方案,但是得到 'TypeError: gapi.auth is undefined -- 我可以在我的代码中哪里使用它? 他们的例子肯定有问题。 30 分钟的快速入门代码和选择器代码一起破解,我至少得到了错误消息。在function onAuthApiLoad()
中,我需要添加gapi.client.init(clientId: clientId, scope: scope);
,我现在使用按钮onclick 调用函数loadPicker
,而不是在加载时自动调用。【参考方案2】:
我现在可以工作了。
在选择器的示例中,https://developers.google.com/picker/docs/,它调用:
<script type="text/javascript" src="https://apis.google.com/js/api.js?onload=onApiLoad"></script>
在这个例子中,https://developers.google.com/api-client-library/javascript/start/start-js,它调用:
<script src="https://apis.google.com/js/client.js?onload=handleClientLoad"></script>
使用 client.js 修复了“TypeError: gapi.auth is undefined”问题,因此登录弹出窗口有效。
也许 api.js 是 API 的旧版本?
【讨论】:
你能详细说明一下吗?我有同样的问题,我不知道你是如何(或是否)使用为响应按钮按下而创建的选择器进行这项工作的。 是的,它可以通过 onclick 按钮事件起作用,请参阅我的问题中的 launchDrive() 函数。对我来说问题是文档不一致,一个说使用 api.js,另一个说使用 client.js,这解决了我的问题。【参考方案3】:直接跳到底部
这是目前适用于我的代码。虽然这是我使用这个 API 的第一个小时,所以我真的不知道这些函数中的任何一个是做什么的,我也不知道正确的顺序和错误处理是什么,但至少现在这是功能性的。也许将来会对其他人有所帮助。
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google Picker Example</title>
</head>
<body style="width: 70%; margin: 100px auto;">
<!-- Added a button to open picker -->
<button onclick="loadPicker();" >Open from GoogleDrive</button>
<div id="result"></div>
<!-- Moved to end of body tag instead of head -->
<script type="text/javascript">
// The Browser API key obtained from the Google API Console.
// Replace with your own Browser API key, or your own key.
var developerKey = '<IDK WHAT'S SUPPOSED TO GO HERE, BUT ITS OK>';
// The Client ID obtained from the Google API Console. Replace with your own Client ID.
var clientId = "<YOUR CLIENT ID GOES HERE>.apps.googleusercontent.com"
// Replace with your own project number from console.developers.google.com.
// See "Project number" under "IAM & Admin" > "Settings"
var appId = "<YOUR APP ID GOES HERE>";
// Scope to use to access user's Drive items.
var scope = ['https://www.googleapis.com/auth/drive'];
var pickerApiLoaded = false;
var oauthToken;
// Use the Google API Loader script to load the google.picker script.
function loadPicker()
// This needs to be client:auth2 no client
gapi.load('client:auth2', 'callback': onAuthApiLoad);
gapi.load('picker', 'callback': onPickerApiLoad);
function onAuthApiLoad()
// we need to init gapi.client with the clientId and scope first
gapi.client.init(
clientId: clientId,
scope: scope
);
// Now we can authorize? seems like the same thing here
window.gapi.auth.authorize(
'client_id': clientId,
'scope': scope,
'immediate': false
,
handleAuthResult);
function onPickerApiLoad()
pickerApiLoaded = true;
createPicker();
function handleAuthResult(authResult)
if (authResult && !authResult.error)
oauthToken = authResult.access_token;
createPicker();
// Create and render a Picker object for searching images.
function createPicker()
// Wow this is a mess
if (pickerApiLoaded && oauthToken)
var view = new google.picker.View(google.picker.ViewId.DOCS);
view.setMimeTypes("image/png,image/jpeg,image/jpg");
var picker = new google.picker.PickerBuilder()
.enableFeature(google.picker.Feature.NAV_HIDDEN)
.enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
.setAppId(appId)
.setOAuthToken(oauthToken)
.addView(view)
.addView(new google.picker.DocsUploadView())
// Guess this is... optional?
//.setDeveloperKey(developerKey)
.setCallback(pickerCallback)
.build();
picker.setVisible(true);
// A simple callback implementation.
function pickerCallback(data)
if (data.action == google.picker.Action.PICKED)
var fileId = data.docs[0].id;
alert('Selected fileId: ' + fileId);
</script>
<!-- The Google API Loader script. Removed the autorun -->
<script type="text/javascript" src="https://apis.google.com/js/api.js"></script>
</body>
</html>
编辑:如果您得到一个未加载的弹出窗口,只需将其关闭并再次单击该按钮。这解决了我刚刚遇到的另一个问题。
再说一次,我还不知道自己在做什么,所以希望我能更好地理解这一点并在以后澄清事情。
E2:啊,关于 OAuth2 的更多信息可以在 Javascript GAPI 文档页面上找到,可以在这里找到:https://developers.google.com/api-client-library/javascript/features/authentication
从另一个文档中,gapi.load('client', callback)
似乎将加载 auth2(如果尚未加载)。调用 gapi.load('client:auth2', callback)
只会保存 1 个网络请求。
注意:当您使用 Oauth 2.0 授权您的应用程序时,您也不需要像第一个示例中那样设置 API 密钥。但是,这样做是一种很好的做法,以防您的代码扩展为处理未经授权的请求。
这就解释了为什么我可以删除 API/开发者密钥。
编辑 3:好的,上面的代码在技术上是错误的。
警告:不要将此方法与推荐的 gapi.auth2.init 和 signIn 流程一起使用。这是两种不同的行为(gapi.auth2.authorize 的授权与 gapi.auth2.init/signIn 的身份验证),如果在同一应用程序中使用,将会出现意外问题。
autorize 用于单次使用身份验证(例如,如果您登录了 2 个 google 帐户)。虽然使用gapi.init()
意味着用于更长期的会话(例如用于登录和退出网站)。
目前这是如何工作的,我不知道。
不要使用上面的代码,只是想记录一下进度。这是一个使用getAuthResponse()
的更好的演示
<html>
<head></head>
<body>
<div style="padding: 50px">
<h2 style="color: #2196f3;">Status: <span id='status'></span></h2>
<button id="signin-button" onclick="handleSignInClick()">Sign In</button>
<button id="signout-button" onclick="handleSignOutClick()">Sign Out</button>
<button id="signout-button" onclick="openFile()">Open File</button>
</div>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script type="text/javascript">
var cid = '<CLIENTID_HERE>';
var scope = 'https://www.googleapis.com/auth/drive';
var authenticated = false;
var pickerLoaded = false;
var auth = null;
var user = null;
var response = null;
var token = null;
var stat = $('#status');
function openFile()
gapi.load('client:auth2', initClient);
gapi.load('picker', onPickerLoad);
function initClient()
stat.html("starting");
gapi.client.init(
clientId: cid,
scope: scope
).then(
function ()
console.log("init");
// Check if we are logged in.
auth = gapi.auth2.getAuthInstance();
auth.isSignedIn.listen(onStatusChange);
authenticated = auth.isSignedIn.get();
stat.html(authenticated);
if (authenticated)
stat.html("Logged In!");
user = auth.currentUser.get();
response = user.getAuthResponse(true);
token = response.access_token;
showPicker();
else
stat.html("Logged Out!");
, function()stat.html("error"));
function onStatusChange(isSignedIn)
if (isSignedIn)
stat.html("Logged In!");
authenticated = true;
user = auth.currentUser.get();
response = user.getAuthResponse(true);
token = response.access_token;
showPicker();
showPicker();
else
authenticated = false;
stat.html("Logged Out!");
function handleSignInClick(event)
gapi.auth2.getAuthInstance().signIn();
function handleSignOutClick(event)
gapi.auth2.getAuthInstance().signOut();
alert("signed out");
function onPickerLoad()
pickerLoaded = true;
showPicker();
function showPicker()
if (pickerLoaded && authenticated)
var view = new google.picker.View(google.picker.ViewId.DOCS);
var picker = new google.picker.PickerBuilder();
picker.addView(view);
picker.enableFeature(google.picker.Feature.MULTISELECT_ENABLED);
picker.setOAuthToken(token);
picker.setAppId()
picker.setCallback(onDriveFileOpen);
picker = picker.build();
picker.setVisible(true);
function onDriveFileOpen(data)
console.log(data);
if (data.action == google.picker.Action.PICKED)
var fileId = data.docs[0].id;
console.log(fileId);
alert(data.docs[0].name);
</script>
<script async defer src="https://apis.google.com/js/api.js">
</script>
</body>
</html>
【讨论】:
【参考方案4】:要解决您的问题,您需要了解 google 在您的情况下如何执行 oauth:
gapi 执行初始化操作 gapi 在新的弹出窗口中打开一个 google auth 页面,您在其中执行登录 登录成功后,gapi 会收到通知,您会收到令牌为什么浏览器会在第二步中阻止弹出窗口:
窗口中不再存在原始事件(window.event
已销毁)。
用户手动阻止了当前站点的弹出窗口
因此,如果用户没有阻止弹出窗口并且您的弹出窗口仍然被阻止,gapi 操作看起来像:
<input type="button" onclick="auth" value="click"/>
function auth()
setTimeout(function()
// by this time window.event is destroyed, that's why browser blocks the popup
window.open(document.URL, '_blank', 'location=yes,height=570,width=520,scrollbars=yes,status=yes');
, 100)
那么你应该怎么做:
确保在单击按钮后不执行任何异步操作,例如 XHRequests 或其他 确保在用户单击按钮时已启动并准备好 gapi,因此当 gapi 需要创建弹出窗口时,window.event
不会为空。所以将所有 gapi init 方法移至DOMContentLoaded
。
作为另一种选择,您可以使用服务器端 oauth 流,这意味着用户将在当前选项卡中被重定向到 gauth 页面,而不是弹出用户。
【讨论】:
以上是关于Google 选择器身份验证弹出窗口被阻止的主要内容,如果未能解决你的问题,请参考以下文章
如何在弹出窗口中通过 OAuth 2.0 向 Google 进行身份验证?
如何将自定义消息添加到 Firebase Google 身份验证弹出窗口?
对 Google 协作平台提要/API 的 CORS 身份验证请求被阻止