使用 JavaScript 授权 Google Drive

IT技术 javascript oauth-2.0 google-drive-api
2021-02-03 05:29:26

我正在尝试授权我的应用程序与 Google Drive 集成。Google 文档提供了有关各种服务器技术的基于服务器的授权和代码示例的详细信息

还有一个支持授权JavaScript Google API 库wiki示例部分,有一个用于创建配置和调用授权函数的代码片段我已将范围更改为我认为驱动所需的范围:

var config = {
    'client_id': 'my_client_ID',
    'scope': 'https://www.googleapis.com/auth/drive.file'
  };
  gapi.auth.authorize(config, function() {
    console.log(gapi.auth);
  });

回调函数永远不会被调用(是的,谷歌 API 库已加载更正)查看 Java 检索和使用 OAuth 2.0 凭据示例,客户端机密似乎是一个参数,这应该进入配置吗?

有没有人在 JS、Drive 或其他 Google API 中尝试过这个?有谁知道调试此类问题的最佳途径,即我是否需要单步执行库并停止抱怨?

请不要建议在服务器端进行授权,我们的应用程序完全是客户端,我不希望服务器上有任何状态(我理解这将导致令牌刷新问题)。我熟悉 Google 控制台中的 API 配置,我相信这和驱动器 SDK 设置是正确的。

2个回答

可以将 Google APIs Javascript 客户端库与 Drive 一起使用,但您必须意识到存在一些痛点。

目前有两个主要问题,它们都有解决方法:

授权

首先,如果您仔细查看 Google Drive 身份验证的工作原理,您会发现,在用户安装您的 Drive 应用程序并尝试使用您的应用程序打开文件或创建新文件后,Drive 会自动启动 OAuth 2.0 授权流程,并且auth 参数设置为response_type=codeaccess_type=offline这基本上意味着现在 Drive 应用程序被迫使用 OAuth 2 服务器端流程,这对 Javascript 客户端库(仅使用客户端流程)没有任何用处。

问题在于:Drive 启动服务器端 OAuth 2.0 流程,然后 Javascript 客户端库启动客户端 OAuth 2.0 流程。

这仍然可以工作,您所要做的就是使用服务器端代码处理 Drive 服务器端流程之后返回的授权代码(您需要将其交换为访问令牌和刷新令牌)。这样,只有在第一个流中才会提示用户进行授权。第一次交换授权码后,认证页面会自动绕过。

我们的文档中提供用于执行此操作的服务器端示例

如果您不在服务器端流程中处理/交换身份验证代码,则用户每次尝试从 Drive 使用您的应用程序时都会被提示进行身份验证。

处理文件内容

第二个问题是我们的 Javascript 客户端库无法轻松上传和访问实际的 Drive 文件内容。您仍然可以这样做,但您必须使用自定义 Javascript 代码。

读取文件内容

当检索文件元数据/文件对象时,它包含一个downloadUrl指向实际文件内容属性。现在可以使用 CORS 请求下载文件,最简单的身份验证方法是在 URL 参数中使用 OAuth 2 访问令牌。因此,只需使用 XHR 或通过将用户转发到 URL附加&access_token=...downloadUrl并获取文件。

上传文件内容

更新来更新:上传端点现在支持CORS。

~~更新:与Drive API的其余部分不同,上传端点不支持CORS,因此您现在必须使用以下技巧:~~

上传文件很棘手,因为它不是内置在 Javascript 客户端库中,而且您不能完全按照本响应中的描述使用 HTTP 来完成,因为我们不允许在这些 API 端点上进行跨域请求。因此,您必须利用我们的 Javascript 客户端库使用的 iframe 代理,并使用它向 Drive SDK 发送构建的多部分请求。感谢@Alain,我们在下面提供了如何执行此操作的示例:

/**
 * Insert new file.
 *
 * @param {File} fileData File object to read data from.
 * @param {Function} callback Callback function to call when the request is complete.
 */
function insertFileData(fileData, callback) {
  const boundary = '-------314159265358979323846';
  const delimiter = "\r\n--" + boundary + "\r\n";
  const close_delim = "\r\n--" + boundary + "--";

  var reader = new FileReader();
  reader.readAsBinaryString(fileData);
  reader.onload = function(e) {
    var contentType = fileData.type || 'application/octet-stream';
    var metadata = {
      'title': fileData.fileName,
      'mimeType': contentType
    };

    var base64Data = btoa(reader.result);
    var multipartRequestBody =
        delimiter +
        'Content-Type: application/json\r\n\r\n' +
        JSON.stringify(metadata) +
        delimiter +
        'Content-Type: ' + contentType + '\r\n' +
        'Content-Transfer-Encoding: base64\r\n' +
        '\r\n' +
        base64Data +
        close_delim;

    var request = gapi.client.request({
        'path': '/upload/drive/v2/files',
        'method': 'POST',
        'params': {'uploadType': 'multipart'},
        'headers': {
          'Content-Type': 'multipart/mixed; boundary="' + boundary + '"'
        },
        'body': multipartRequestBody});
    if (!callback) {
      callback = function(file) {
        console.log(file)
      };
    }
    request.execute(callback);
  }
}

为了改善这一切,未来我们可能会:

  • 让开发人员选择他们想要使用的 OAuth 2.0 流程(服务器端或客户端)或让开发人员完全处理 OAuth 流程。
  • 允许/upload/...端点上的 CORS
  • 允许exportLinks本地 gDocs上的 CORS
  • 我们应该使用我们的 Javascript 客户端库更容易上传文件。

不过此时没有Promise:)

能够选择流量会很棒。对此的预计到达时间会更大!
2021-03-14 05:29:26
@David 我已经更新了关于使用 Javascript 客户端库的答案,因为我意识到大多数问题可能都有解决方法。如果您还有问题,请告诉我。
2021-03-20 05:29:26
更新:我们为下载 URL 启用了 CORS(大约 2-3 周前)。我正在更改响应
2021-03-23 05:29:26
更新:您可以使用 ?access_token=... URL 参数来授权下载 URL,从而更轻松地下载或流式传输云端硬盘上的私人文件。
2021-04-05 05:29:26
更新:CORS 似乎不适用于 /upload/ 端点。现在记录下来。
2021-04-06 05:29:26

我做的。这是我的代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset='utf-8' />
    <style>
        p {         
            font-family: Tahoma;
        }
    </style>
  </head>
  <body>
    <!--Add a button for the user to click to initiate auth sequence -->
    <button id="authorize-button" style="visibility: hidden">Authorize</button>
    <script type="text/javascript">
      var clientId = '######';
      var apiKey = 'aaaaaaaaaaaaaaaaaaa';
      // To enter one or more authentication scopes, refer to the documentation for the API.
      var scopes = 'https://www.googleapis.com/auth/drive';

      // Use a button to handle authentication the first time.
      function handleClientLoad() {
        gapi.client.setApiKey(apiKey);
        window.setTimeout(checkAuth,1);
      }

      function checkAuth() {
        gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: true}, handleAuthResult);
      }

      function handleAuthResult(authResult) {
        var authorizeButton = document.getElementById('authorize-button');
        if (authResult && !authResult.error) {
          authorizeButton.style.visibility = 'hidden';
          makeApiCall();
        } else {
          authorizeButton.style.visibility = '';
          authorizeButton.onclick = handleAuthClick;
        }
      }

      function handleAuthClick(event) {
        gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, handleAuthResult);
        return false;
      }

      // Load the API and make an API call.  Display the results on the screen.
      function makeApiCall() {
        gapi.client.load('drive', 'v2', function() {

          var request = gapi.client.drive.files.list ( {'maxResults': 5 } );

          request.execute(function(resp) {          
            for (i=0; i<resp.items.length; i++) {
                    var titulo = resp.items[i].title;
                    var fechaUpd = resp.items[i].modifiedDate;
                    var userUpd = resp.items[i].lastModifyingUserName;

                    var fileInfo = document.createElement('li');
                    fileInfo.appendChild(document.createTextNode('TITLE: ' + titulo + ' - LAST MODIF: ' + fechaUpd + ' - BY: ' + userUpd ));                
                    document.getElementById('content').appendChild(fileInfo);
            }
          });        
        });
      }
    </script>
    <script src="https://apis.google.com/js/client.js?onload=handleClientLoad"></script>    
    <p><b>These are 5 files from your GDrive :)</b></p>
    <div id="content"></div>
  </body>
</html>

你只需要改变:

  • var clientId = '######';
  • var apiKey = 'aaaaaaaaaaaaaaaaaa';

从您的 Google API 控制台到您的 clientID 和 ApiKey :)

当然,您必须在 Google API Console 上创建您的项目,激活 Drive API 并激活 OAuth 2.0 中的 Google Accounts 身份验证(真的很简单!)

PS:它不能在您的 PC 本地运行,它可以在某些主机上运行,​​并且必须在项目控制台上提供它的 url :)

此解决方案是否适用于 Chrome 应用程序或扩展程序?
2021-03-20 05:29:26
我现在不知道,这是 2012 年的解决方案:(
2021-03-26 05:29:26
可以在本地 PC 上进行测试。在 API 控制台中将 mylocalpc.example.com 注册为 Javascript 源编辑您的 /etc/hosts(或任何等效的windoze)以在 127.0.0.1 的条目之后添加 mylocalpc.example.com 我正在运行我的本地测试GAE 测试服务器,所以我需要将端口 8888 作为 Javascript 原始 url 的一部分,即。" mylocalpc.example.com:8888 "
2021-04-05 05:29:26
我激活了所有服务,但仍然没有配置访问权限。请使用 Google Developers Console 为您的项目激活 API。
2021-04-13 05:29:26