使用用户账号对安装式应用进行身份验证
本指南介绍了在您的应用已安装到用户机器上的情况下,如何使用用户账号执行身份验证,以获得 BigQuery API 的访问权限。
如需确保应用仅访问最终用户可用的 BigQuery 表,请使用用户凭据进行身份验证。用户凭据只能对最终用户的 Google Cloud 项目(而非应用的项目)运行查询。因此,系统会按查询(而非应用)向用户收取费用。
准备工作
- 创建一个代表已安装的应用的 Google Cloud 项目。
- 安装 BigQuery 客户端库。
-
安装身份验证库。
Java
如果您使用的是 Maven,请在 Pom 文件中添加以下依赖项。
<dependency> <groupId>com.google.oauth-client</groupId> <artifactId>google-oauth-client-java6</artifactId> <version>1.31.0</version> </dependency> <dependency> <groupId>com.google.oauth-client</groupId> <artifactId>google-oauth-client-jetty</artifactId> <version>1.31.0</version> </dependency>
Python
安装用于 Google 身份验证的 oauthlib 集成。
pip install --upgrade google-auth-oauthlib
Node.js
安装用于 Google 身份验证的 oauthlib 集成。
npm install google-auth-library
npm install readline-promise
设置您的客户端凭据
使用以下按钮选择项目并创建所需的凭据。手动创建凭据
- 进入 Google Cloud 控制台中的凭据页面。
- 填写 OAuth 同意屏幕中的必填字段。
- 在凭据页面上,点击创建凭据按钮。
选择 OAuth 客户端 ID。
- 选择桌面设备作为应用类型,然后点击创建。
-
点击下载 JSON 按钮以下载凭据。
将凭据文件保存到
client_secrets.json
。此文件必须随应用一起分发。
API 的身份验证与调用
-
使用客户端凭据执行 OAuth 2.0 流程。
Java
import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.store.FileDataStoreFactory; import com.google.api.gax.paging.Page; import com.google.auth.oauth2.GoogleCredentials; import com.google.auth.oauth2.UserCredentials; import com.google.cloud.bigquery.BigQuery; import com.google.cloud.bigquery.BigQueryException; import com.google.cloud.bigquery.BigQueryOptions; import com.google.cloud.bigquery.Dataset; import com.google.common.collect.ImmutableList; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.GeneralSecurityException; import java.util.List; // Sample to authenticate by using a user credential public class AuthUserFlow { private static final File DATA_STORE_DIR = new File(AuthUserFlow.class.getResource("/").getPath(), "credentials"); private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); // i.e redirect_uri http://localhost:61984/Callback private static final int LOCAL_RECEIVER_PORT = 61984; public static void runAuthUserFlow() { // TODO(developer): Replace these variables before running the sample. /** * Download your OAuth2 configuration from the Google Developers Console API Credentials page. * https://console.cloud.google.com/apis/credentials */ Path credentialsPath = Paths.get("path/to/your/client_secret.json"); List<String> scopes = ImmutableList.of("https://www.googleapis.com/auth/bigquery"); authUserFlow(credentialsPath, scopes); } public static void authUserFlow(Path credentialsPath, List<String> selectedScopes) { // Reading credentials file try (InputStream inputStream = Files.newInputStream(credentialsPath)) { // Load client_secret.json file GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(inputStream)); String clientId = clientSecrets.getDetails().getClientId(); String clientSecret = clientSecrets.getDetails().getClientSecret(); // Generate the url that will be used for the consent dialog. GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder( GoogleNetHttpTransport.newTrustedTransport(), JSON_FACTORY, clientSecrets, selectedScopes) .setDataStoreFactory(new FileDataStoreFactory(DATA_STORE_DIR)) .setAccessType("offline") .setApprovalPrompt("auto") .build(); // Exchange an authorization code for refresh token LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(LOCAL_RECEIVER_PORT).build(); Credential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize("user"); // OAuth2 Credentials representing a user's identity and consent GoogleCredentials credentials = UserCredentials.newBuilder() .setClientId(clientId) .setClientSecret(clientSecret) .setRefreshToken(credential.getRefreshToken()) .build(); // Initialize client that will be used to send requests. This client only needs to be created // once, and can be reused for multiple requests. BigQuery bigquery = BigQueryOptions.newBuilder().setCredentials(credentials).build().getService(); Page<Dataset> datasets = bigquery.listDatasets(BigQuery.DatasetListOption.pageSize(100)); if (datasets == null) { System.out.println("Dataset does not contain any models"); return; } datasets .iterateAll() .forEach( dataset -> System.out.printf("Success! Dataset ID: %s ", dataset.getDatasetId())); } catch (BigQueryException | IOException | GeneralSecurityException ex) { System.out.println("Project does not contain any datasets \n" + ex.toString()); } } }
Python
from google_auth_oauthlib import flow # A local server is used as the callback URL in the auth flow. appflow = flow.InstalledAppFlow.from_client_secrets_file( "client_secrets.json", scopes=["https://www.googleapis.com/auth/bigquery"] ) # This launches a local server to be used as the callback URL in the desktop # app auth flow. If you are accessing the application remotely, such as over # SSH or a remote Jupyter notebook, this flow will not work. Use the # `gcloud auth application-default login --no-browser` command or workload # identity federation to get authentication tokens, instead. # appflow.run_local_server() credentials = appflow.credentials
Node.js
const {OAuth2Client} = require('google-auth-library'); const readline = require('readline-promise').default; function startRl() { const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); return rl; } /** * Download your OAuth2 configuration from the Google * Developers Console API Credentials page. * https://console.cloud.google.com/apis/credentials */ const keys = require('./oauth2.keys.json'); /** * Create a new OAuth2Client, and go through the OAuth2 content * workflow. Return the full client to the callback. */ async function getRedirectUrl() { const rl = main.startRl(); // Create an oAuth client to authorize the API call. Secrets are kept in a `keys.json` file, // which should be downloaded from the Google Developers Console. const oAuth2Client = new OAuth2Client( keys.installed.client_id, keys.installed.client_secret, keys.installed.redirect_uris[0] ); // Generate the url that will be used for the consent dialog. const authorizeUrl = oAuth2Client.generateAuthUrl({ access_type: 'offline', scope: 'https://www.googleapis.com/auth/bigquery', prompt: 'consent', }); console.info( `Please visit this URL to authorize this application: ${authorizeUrl}` ); const code = await rl.questionAsync('Enter the authorization code: '); const tokens = await main.exchangeCode(code); rl.close(); return tokens; } // Exchange an authorization code for an access token async function exchangeCode(code) { const oAuth2Client = new OAuth2Client( keys.installed.client_id, keys.installed.client_secret, keys.installed.redirect_uris[0] ); const r = await oAuth2Client.getToken(code); console.info(r.tokens); return r.tokens; } async function authFlow(projectId = 'project_id') { /** * TODO(developer): * Save Project ID as environment variable PROJECT_ID="project_id" * Uncomment the following line before running the sample. */ // projectId = process.env.PROJECT_ID; const tokens = await main.getRedirectUrl(); const credentials = { type: 'authorized_user', client_id: keys.installed.client_id, client_secret: keys.installed.client_secret, refresh_token: tokens.refresh_token, }; return { projectId, credentials, }; }
- 使用经过身份验证的凭据连接到 BigQuery API。
Java
import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.store.FileDataStoreFactory; import com.google.auth.oauth2.GoogleCredentials; import com.google.auth.oauth2.UserCredentials; import com.google.cloud.bigquery.BigQuery; import com.google.cloud.bigquery.BigQueryException; import com.google.cloud.bigquery.BigQueryOptions; import com.google.cloud.bigquery.QueryJobConfiguration; import com.google.cloud.bigquery.TableResult; import com.google.common.collect.ImmutableList; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.security.GeneralSecurityException; import java.util.List; // Sample to query by using a user credential public class AuthUserQuery { private static final File DATA_STORE_DIR = new File(AuthUserQuery.class.getResource("/").getPath(), "credentials"); private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); // i.e redirect_uri http://localhost:61984/Callback private static final int LOCAL_RECEIVER_PORT = 61984; public static void runAuthUserQuery() { // TODO(developer): Replace these variables before running the sample. /** * Download your OAuth2 configuration from the Google Developers Console API Credentials page. * https://console.cloud.google.com/apis/credentials */ Path credentialsPath = Paths.get("path/to/your/client_secret.json"); List<String> scopes = ImmutableList.of("https://www.googleapis.com/auth/bigquery"); String query = "SELECT name, SUM(number) as total" + " FROM `bigquery-public-data.usa_names.usa_1910_current`" + " WHERE name = 'William'" + " GROUP BY name;"; authUserQuery(credentialsPath, scopes, query); } public static void authUserQuery( Path credentialsPath, List<String> selectedScopes, String query) { // Reading credentials file try (InputStream inputStream = Files.newInputStream(credentialsPath)) { // Load client_secret.json file GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(inputStream)); String clientId = clientSecrets.getDetails().getClientId(); String clientSecret = clientSecrets.getDetails().getClientSecret(); // Generate the url that will be used for the consent dialog. GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder( GoogleNetHttpTransport.newTrustedTransport(), JSON_FACTORY, clientSecrets, selectedScopes) .setDataStoreFactory(new FileDataStoreFactory(DATA_STORE_DIR)) .setAccessType("offline") .setApprovalPrompt("auto") .build(); // Exchange an authorization code for refresh token LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(LOCAL_RECEIVER_PORT).build(); Credential credential = new AuthorizationCodeInstalledApp(flow, receiver).authorize("user"); // OAuth2 Credentials representing a user's identity and consent GoogleCredentials credentials = UserCredentials.newBuilder() .setClientId(clientId) .setClientSecret(clientSecret) .setRefreshToken(credential.getRefreshToken()) .build(); // Initialize client that will be used to send requests. This client only needs to be created // once, and can be reused for multiple requests. BigQuery bigquery = BigQueryOptions.newBuilder().setCredentials(credentials).build().getService(); QueryJobConfiguration queryConfig = QueryJobConfiguration.newBuilder(query).build(); TableResult results = bigquery.query(queryConfig); results .iterateAll() .forEach(row -> row.forEach(val -> System.out.printf("%s,", val.toString()))); System.out.println("Query performed successfully."); } catch (BigQueryException | IOException | GeneralSecurityException | InterruptedException ex) { System.out.println("Query not performed \n" + ex.toString()); } } }
Python
from google.cloud import bigquery # TODO: Uncomment the line below to set the `project` variable. # project = 'user-project-id' # # The `project` variable defines the project to be billed for query # processing. The user must have the bigquery.jobs.create permission on # this project to run a query. See: # https://cloud.google.com/bigquery/docs/access-control#permissions client = bigquery.Client(project=project, credentials=credentials) query_string = """SELECT name, SUM(number) as total FROM `bigquery-public-data.usa_names.usa_1910_current` WHERE name = 'William' GROUP BY name; """ results = client.query_and_wait(query_string) # Print the results. for row in results: # Wait for the job to complete. print("{}: {}".format(row["name"], row["total"]))
Node.js
async function query() { const {BigQuery} = require('@google-cloud/bigquery'); const credentials = await main.authFlow(); const bigquery = new BigQuery(credentials); // Queries the U.S. given names dataset for the state of Texas. const query = `SELECT name, SUM(number) as total FROM \`bigquery-public-data.usa_names.usa_1910_current\` WHERE name = 'William' GROUP BY name;`; // For all options, see https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query const options = { query: query, }; // Run the query as a job const [job] = await bigquery.createQueryJob(options); console.log(`Job ${job.id} started.`); // Wait for the query to finish const [rows] = await job.getQueryResults(); // Print the results console.log('Rows:'); rows.forEach(row => console.log(row)); return rows; } const main = { query, authFlow, exchangeCode, getRedirectUrl, startRl, }; module.exports = { main, }; if (module === require.main) { query().catch(console.error); }
当您运行示例代码时,它将启动一个浏览器,请求授予权限来访问与客户端密钥关联的项目。您可以使用所得到的凭据访问用户的 BigQuery 资源,因为示例代码请求的是 BigQuery 范围。