java api中的Bigquery实例是不是在使用具有刷新令牌的凭据构建它后自动刷新它的Oauth 2.0授权?

Posted

技术标签:

【中文标题】java api中的Bigquery实例是不是在使用具有刷新令牌的凭据构建它后自动刷新它的Oauth 2.0授权?【英文标题】:Does Bigquery instance in java api refresh it's Oauth 2.0 authorization automatically after building it with a credential that has a refresh token?java api中的Bigquery实例是否在使用具有刷新令牌的凭据构建它后自动刷新它的Oauth 2.0授权? 【发布时间】:2012-08-25 15:23:35 【问题描述】:

早安,

我最近正在通过 java api 为 bigquery 测试 Oauth2.0 安装的应用程序身份验证。 我用了 this command line sample 作为一个来源,因为我想让用户更容易获得访问权限(要求他们复制代码会很丑)

如果我在 GoogleAuthorizationCodeflow 中使用 SetAccessType(Offline),它会自动为我刷新访问令牌。

我想问一下,如果我授权一个 com.google.api.services.bigquery.Bigquery 实例 com.google.api.services.bigquery.Bigquery.Builder.Builder(HttpTransport 传输,JsonFactory jsonFactory,HttpRequestInitializer httpRequestInitializer) 使用 具有刷新令牌的凭据,我是否必须重新授权它或者它会更新自身内部的凭据?

如果它在一小时后没有给我一个到 bigquery 的有效连接(令牌过期时间) 那么重新授权客户端的最佳方法是什么?

我应该使用 credentials.RefreshToken() 然后用新的访问令牌构建另一个 bigquery 实例,还是有更好的方法?

(我可能还想更改凭据 Refreshlistener,但问题是,如果我用 GoogleAuthorizationCodeflow.createAndStoreCredential(response, Clientid) 实例化凭据之后,我将无法获得 Refreshlistener。我怎样才能得到它,所以我可以使用该类自动重新授权我的 Bigquery 客户端

代码流将刷新的访问令牌存储在哪里(如果它自动刷新)? 如果我这样声明:

new GoogleAuthorizationCodeFlow.Builder(
              transport, jsonFactory, clientSecrets, scopes).setAccessType("offline")
              .setApprovalPrompt("auto").setCredentialStore(new MemoryCredentialStore()).build();

它会始终将我刷新的访问令牌存储在 MemoryCredentialStore 中吗? 因此,如果我使用 .getCredentialStore().load(userId, credential) 它将从 memorystore 加载刷新的访问令牌?

有没有办法增加或减少访问令牌有效的时间?因为出于测试目的,我真的很想这样做。

ps:我也在研究这个 google-api-java-client 的源代码 和 google-oauth-java-client 但我仍然找不到解决方案。

最重要的是,我正在研究:class Credential 代码: 公共无效拦截(HttpRequest 请求)抛出 IOException 锁.lock();... 但是我实际上无法弄清楚何时调用此方法,最终我迷失了这个问题。

期待您的回答: 阿提拉

【问题讨论】:

【参考方案1】:

这里有很多问题。您将需要使用 GoogleCredential 类从现有的刷新令牌中获取新的访问令牌,您需要存储该令牌(使用您想要的任何方法,例如 MemoryCredentialStore)。

访问令牌仅持续一个小时,无法更改。

以下是使用已安装流程的示例:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Collections;
import java.util.List;
import java.util.Properties;

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.auth.oauth2.TokenResponse;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;
import com.google.api.services.bigquery.Bigquery;
import com.google.api.services.bigquery.Bigquery.Datasets;
import com.google.api.services.bigquery.BigqueryScopes;
import com.google.api.services.bigquery.model.DatasetList;


class BigQueryInstalledAuthDemo 

  // Change this to your current project ID
  private static final String PROJECT_NUMBER = "XXXXXXXXXX";

  // Load Client ID/secret from client_secrets.json file.
  private static final String CLIENTSECRETS_LOCATION = "client_secrets.json";
  static GoogleClientSecrets clientSecrets = loadClientSecrets();

  private static final String REDIRECT_URI = "urn:ietf:wg:oauth:2.0:oob";

  // Objects for handling HTTP transport and JSON formatting of API calls
  private static final HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
  private static final JsonFactory JSON_FACTORY = new JacksonFactory();

  private static GoogleAuthorizationCodeFlow flow = null;

  // BigQuery Client
  static Bigquery bigquery;


  public static void main(String[] args) throws IOException 

    // Attempt to Load existing Refresh Token
    String storedRefreshToken = loadRefreshToken();

    // Check to see if the an existing refresh token was loaded.
    // If so, create a credential and call refreshToken() to get a new
    // access token.
    if (storedRefreshToken != null) 

      // Request a new Access token using the refresh token.
      GoogleCredential credential = createCredentialWithRefreshToken(
          HTTP_TRANSPORT, JSON_FACTORY, new TokenResponse().setRefreshToken(storedRefreshToken));
      credential.refreshToken();

      bigquery = buildService(credential);

    // If there is no refresh token (or token.properties file), start the OAuth
    // authorization flow.
     else 
      String authorizeUrl = new GoogleAuthorizationCodeRequestUrl(
          clientSecrets,
          REDIRECT_URI,
          Collections.singleton(BigqueryScopes.BIGQUERY)).setState("").build();

      System.out.println("Paste this URL into a web browser to authorize BigQuery Access:\n" + authorizeUrl);

      System.out.println("... and type the code you received here: ");
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
      String authorizationCode = in.readLine();

      // Exchange the auth code for an access token and refesh token
      Credential credential = exchangeCode(authorizationCode);

      // Store the refresh token for future use.
      storeRefreshToken(credential.getRefreshToken());

      bigquery = buildService(credential);
    

    // Make API calls using your client.
    listDatasets(bigquery, PROJECT_NUMBER);

  


  /**
   *  Builds an authorized BigQuery API client.
   */
  private static Bigquery buildService(Credential credential) 
    return new Bigquery.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential).build();
  


  /**
   * Build an authorization flow and store it as a static class attribute.
   */
  static GoogleAuthorizationCodeFlow getFlow() 
    if (flow == null) 
      flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT,
          JSON_FACTORY,
          clientSecrets,
          Collections.singleton(BigqueryScopes.BIGQUERY))
      .setAccessType("offline").setApprovalPrompt("force").build();
    
    return flow;
  


  /**
   * Exchange the authorization code for OAuth 2.0 credentials.
   */
  static Credential exchangeCode(String authorizationCode) throws IOException  
    GoogleAuthorizationCodeFlow flow = getFlow();
    GoogleTokenResponse response =
        flow.newTokenRequest(authorizationCode).setRedirectUri(REDIRECT_URI).execute();
    return flow.createAndStoreCredential(response, null);
  


  /**
   * No need to go through OAuth dance, get an access token using the
   * existing refresh token.
   */
  public static GoogleCredential createCredentialWithRefreshToken(HttpTransport transport,
      JsonFactory jsonFactory, TokenResponse tokenResponse) 
    return new GoogleCredential.Builder().setTransport(transport)
        .setJsonFactory(jsonFactory)
        .setClientSecrets(clientSecrets)
        .build()
        .setFromTokenResponse(tokenResponse);
  


  /**
   *  Helper to load client ID/Secret from file.
   */
  private static GoogleClientSecrets loadClientSecrets() 
    try 
      GoogleClientSecrets clientSecrets =
          GoogleClientSecrets.load(new JacksonFactory(),
              BigQueryInstalledAuthDemo.class.getResourceAsStream(CLIENTSECRETS_LOCATION));
      return clientSecrets;
     catch (Exception e)  
      System.out.println("Could not load clientsecrets.json");
      e.printStackTrace();
    
    return clientSecrets;
  


  /**
   *  Helper to store a new refresh token in token.properties file.
   */
  private static void storeRefreshToken(String refresh_token) 
    Properties properties = new Properties();
    properties.setProperty("refreshtoken", refresh_token);
    System.out.println(properties.get("refreshtoken"));
    try 
      properties.store(new FileOutputStream("token.properties"), null);
     catch (FileNotFoundException e) 
      e.printStackTrace();
     catch (IOException e) 
      e.printStackTrace();
    
  


  /**
   *  Helper to load refresh token from the token.properties file.
   */
  private static String loadRefreshToken()
    Properties properties = new Properties();
    try 
      properties.load(new FileInputStream("token.properties"));
     catch (FileNotFoundException e) 
      e.printStackTrace();
     catch (IOException e) 
      e.printStackTrace();
    
    return (String) properties.get("refreshtoken");
  


  /**
   *
   * List available Datasets.
   */
  public static void listDatasets(Bigquery bigquery, String projectId)
      throws IOException 
    Datasets.List datasetRequest = bigquery.datasets().list(projectId);
    DatasetList datasetList = datasetRequest.execute();
    if (datasetList.getDatasets() != null) 
      List<DatasetList.Datasets> datasets = datasetList.getDatasets();
      System.out.println("Available datasets\n----------------");
      for (com.google.api.services.bigquery.model.DatasetList.Datasets dataset : datasets) 
        System.out.format("%s\n", dataset.getDatasetReference().getDatasetId());
      
    
  


通过存储和使用刷新令牌在已安装的应用程序中获取新访问令牌的授权替代方法是使用server to server service account authorization 流。在这种情况下,您的应用程序需要能够安全地存储和使用唯一的私钥。以下是使用 Google Java API 客户端的此类流程示例:

import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson.JacksonFactory;

import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.services.bigquery.Bigquery;
import com.google.api.services.bigquery.Bigquery.Datasets;
import com.google.api.services.bigquery.model.DatasetList;

import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;

public class JavaCommandLineServiceAccounts 

  private static final String SCOPE = "https://www.googleapis.com/auth/bigquery";
  private static final HttpTransport TRANSPORT = new NetHttpTransport();
  private static final JsonFactory JSON_FACTORY = new JacksonFactory();

  private static Bigquery bigquery;

  public static void main(String[] args) throws IOException, GeneralSecurityException 
    GoogleCredential credential = new GoogleCredential.Builder().setTransport(TRANSPORT)
        .setJsonFactory(JSON_FACTORY)
        .setServiceAccountId("XXXXXXX@developer.gserviceaccount.com")
        .setServiceAccountScopes(SCOPE)
        .setServiceAccountPrivateKeyFromP12File(new File("my_file.p12"))
        .build();

    bigquery = new Bigquery.Builder(TRANSPORT, JSON_FACTORY, credential)
        .setApplicationName("BigQuery-Service-Accounts/0.1")
        .setHttpRequestInitializer(credential).build();

    Datasets.List datasetRequest = bigquery.datasets().list("publicdata");
    DatasetList datasetList = datasetRequest.execute();
    System.out.format("%s\n", datasetList.toPrettyString());
  


【讨论】:

非常感谢您发布服务器到服务器身份验证的代码,我们已经对此进行了研究,但是,该解决方案不是我们想要使用的,因为它使用文件进行身份验证并且因为它是一个服务帐户,无法根据数据集设置权限。 关于另一个授权示例,这正是我们所使用的,并非完全相同,但我们使用已安装的应用程序 id 和 clientsecret 来获取授权流的凭据(setaccestype(offline))和我们已经使用涉及刷新和访问令牌的凭据构建了 bigquery 实例。我的问题更可能是关于我们是否应该始终使用通过 credentials.refreshToken() 获得的新访问令牌手动重建与 bigquery 的连接,还是 bigquery api 自行完成? 幸运的是,在我们进行了 2 小时的 Junit 测试以对其进行测试之后,因为它工作得很好,无需与凭据或内置的 bigquery 连接进行任何手动交互,我猜答案是它确实使用自动刷新令牌以刷新与 bigquery 的连接。非常感谢你真的试图帮助我。有没有办法实时联系 bigquery 支持,例如聊天或语音聊天?在我们的开发项目中存在很多小问题,如果及时回答,我们可以优雅地缩短开发时间。再次感谢您

以上是关于java api中的Bigquery实例是不是在使用具有刷新令牌的凭据构建它后自动刷新它的Oauth 2.0授权?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Python BigQuery API 追加到 BigQuery 中的表

用于读取记录数组的 BigQuery Java API:“不支持按名称检索字段值”异常

谷歌 API Bigquery 空指针 java

如何在 Java 中为 BigQuery API 请求设置超时

java.lang.NoSuchMethodError: com.google.api.services.bigquery.model.JobConfigurationQuery.getConnect

通过 BigQuery Java api 创建数据存储备份表