Google Drive API - OAuth2.0:如何自动化身份验证过程?疑问和问题

Google Drive API - OAuth2.0: How to Automate Authentication Process? Doubts and Questions

我正在尝试将 Google APIs 集成到一个项目(Thesis project)中,我有一些疑问和问题。所以,这是场景:

我在 Java 中编写了一个后端应用程序,它仅从命令行运行并且完全不与用户交互。它的目标是允许传感器和执行器之间的通信和交互。一切都很好。现在我想集成一些东西,以便让传感器以一定的周期和检测到的阈值备份数据。所以我想,为什么不尝试使用 Google 驱动器。第一个非常有用的链接是:

https://developers.google.com/drive/web/quickstart/quickstart-java

https://developers.google.com/accounts/docs/OAuth2InstalledApp

快速入门示例非常有用。然而,它需要相当多的设置:在 Developer Console 中创建一个项目(因此是一个帐户),启用 Drive API,然后创建一个 Client ID 和一个 Client Secret。完成这些步骤后,您可以对客户端 ID 和密码进行硬编码,以形成对 google 驱动器的请求 URL。然后,请您在浏览器中输入 url,如果不是,请登录,接受并最后将用于获取访问令牌的授权代码复制并粘贴到您的控制台中。哇,相当安全的过程。但是,嘿,我完全同意它,最重要的是在我们有一个网络应用程序、智能手机应用程序或一个需要用户身份验证和授权才能让应用程序完成其工作的网络服务的情况下 通过访问其他人的帐户。但就我而言,我只是希望传感器能够在 my google 驱动器上备份数据。

这些事实引出了我的第一个问题:为了使用 Google APIs(在本例中为驱动器),我是否必须创建一个项目?还是有另一种方法?如果我没记错的话,没有其他方法可以在不在 Developer Console 中创建项目的情况下创建客户端 ID 和密码。这让我很困惑。为什么我要创建一个项目来基本上使用一些库?

那么,让我们假设前面的限制是合理的,然后继续讨论真正的问题:如何使身份验证过程自动化?在我的场景中,传感器(只是一个 Java 模块)想要备份数据,不可能完成所有这些步骤。关于 OAuth 2.0 的 google 页面对我们可以嵌入身份验证过程的不同场景有很好的解释,其中包括 "devices with limited input capabilities"。不幸的是,这比其他的更复杂,需要 "The user switches to a device or computer with richer input capabilities, launches a browser, navigates to the URL specified on the limited-input device, logs in, and enters the code." (LOL)

所以,我没有放弃,我最终在谈论 OAuth Playground 的 post 上结束了:How do I authorise an app (web or installed) without user intervention? (canonical ?)。它看起来真的像是我的解决方案,特别是当它说:

NB2. This technique works well if you want a web app which access your own (and only your own) Drive account, without bothering to write the authorization code which would only ever be run once. Just skip step 1, and replace "my.drive.app" with your own email address in step 5.

但是,如果我没记错的话,我认为 OAuth Playground 只是为了帮助测试和调试使用 Google API 的项目,不是吗?而且,Google驱动类如GoogleAuthorizationCodeFlowGoogleCredential(在 Java 快速入门示例中使用)总是需要 Client ID、Client Secret 等等,这使我指向零(创建一个项目并完成整个图形过程)。

结论:有没有办法避免 "graphical" 身份验证交互并将其转换为仅使用 Drive 的 API 的自动化过程而无需用户干预?非常感谢,我将不胜感激任何提示、提示、答案、指针:-)

我在 How do I authorise an app (web or installed) without user intervention? (canonical ?)

写了 SO post

它描述的确实是您用例的解决方案。您错过的关键是第 7 步,在该步骤中,您将自己的应用程序的详细信息输入到 OAuth Playground 中。从那时起,playground 将模拟您的应用程序,因此您可以进行一次性授权并获取刷新令牌。

这只是我根据 pinoyyid 的建议编写的一小段代码。只是回顾一下我们在这种情况下应该做什么(当您的程序中没有用于完成所有 Google GUI 身份验证过程的用户交互时)。据https://developers.google.com/drive/web/quickstart/quickstart-java

报道
  1. 转到 Google 开发者控制台。
  2. Select一个项目,或者创建一个新项目。
  3. 在左侧边栏中,展开 APIs & auth。接下来,单击 APIs。在 APIs 的列表中,确保驱动器 API.
  4. 的状态为 ON
  5. 在左侧边栏中,select 凭据。

无论哪种情况,您最终都会进入凭据页面并可以从此处创建项目的凭据。

在凭据页面中,单击 OAuth 标题下的创建新客户端 ID 以创建您的 OAuth 2.0 凭据。您的应用程序的客户端 ID、电子邮件地址、客户端密码、重定向 URI 和 Java 脚本来源位于 web 应用程序的客户端 ID 部分。

pinoyyd post 更简洁,直截了当:How do I authorise a background web app without user intervention? (canonical ?) 注意第7步

最后这段代码非常简单,就是发送一个 POST 请求,在 Java 中可以通过多种方式实现。因此,这只是一个示例,我相信还有改进的余地 ;-)

// Both to set access token the first time that we run the module and in general to refresh the token
public void sendPOST(){
  try {
      URL url = new URL("https://www.googleapis.com/oauth2/v3/token");
      Map<String,Object> params = new LinkedHashMap<>();
      params.put("client_id", CLIENT_ID);
      params.put("client_secret", CLIENT_SECRET);
      params.put("refresh_token", REFRESH_TOKEN);
      params.put("grant_type", "refresh_token");

      StringBuilder postData = new StringBuilder();
      for (Map.Entry<String,Object> param : params.entrySet()) {
          if (postData.length() != 0) postData.append('&');
          postData.append(URLEncoder.encode(param.getKey(), "UTF-8"));
          postData.append('=');
          postData.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
      }
      byte[] postDataBytes = postData.toString().getBytes("UTF-8");

      HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
      conn.setRequestMethod("POST");
      conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
      conn.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
      conn.setDoOutput(true);
      conn.getOutputStream().write(postDataBytes);

      BufferedReader in_rd = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));

      // Read response body which should be a json structure
      String inputLine;
      StringBuilder responseBody = new StringBuilder();

      while ((inputLine = in_rd.readLine()) != null) {
              responseBody.append(inputLine);
      }
      in_rd.close();

      //Parsing Response --> create a json object
      JSONObject jsonResp = new JSONObject(responseBody);

      //Modify previous access token String
      ACCESS_TOKEN = jsonResp.getString("access_token");

  }

  catch(MalformedURLException ex_URL){
      System.out.println("An error occured: " + ex_URL.getMessage());
  }
  catch(JSONException ex_json) {
      System.out.println("An error occured: " + ex_json.getMessage());             
  }
  catch(IOException ex_IO){
      System.out.println("An error occured: " + ex_IO.getMessage());
  }

} //end of sendRefreshPOST method

希望这段代码能帮助其他面临同样情况的人!