【问题标题】:Google picker auth popup is being blockedGoogle 选择器身份验证弹出窗口被阻止
【发布时间】: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});
    }

【问题讨论】:

    标签: javascript oauth popup google-drive-api


    【解决方案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 页面,而不是弹出用户。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-10-25
        • 2020-08-28
        • 1970-01-01
        • 2019-05-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多