本教程旨在指导 java 应用程序如何通过 google oauth 2.0 授权代码流获取用户访问令牌。针对直接使用 `googlecredentials.builder` 遇到的权限问题,本文将介绍正确的依赖配置、`client_secrets.json` 文件创建,并提供使用 `googleauthorizationcodeflow` 实现交互式用户认证的完整代码示例,最终安全地获取并管理访问令牌,助力开发者顺利集成 google api。
在 Java 应用程序中与 Google API 进行交互时,获取用户授权的访问令牌是关键一步。许多开发者在尝试直接使用 com.google.auth.oauth2.GoogleCredentials.Builder 配合 clientId 和 clientSecret 获取访问令牌时,可能会遇到 Builder() 方法受保护的访问权限问题。这是因为 GoogleCredentials.Builder 主要设计用于服务账户认证或在已拥有刷新令牌等凭据时构建 GoogleCredentials 对象,而非用于启动交互式的用户授权流程。
对于需要用户通过浏览器进行认证并授予应用程序访问权限的场景(例如桌面应用程序或自动化脚本),Google 推荐使用 OAuth 2.0 授权代码流(Authorization Code Flow)。本文将详细介绍如何正确配置项目、实现这一流程,并安全地获取所需的 Google 访问令牌。
为了实现 Google OAuth 2.0 授权代码流,我们需要在项目的 pom.xml 文件中添加以下 Maven 依赖。这些库提供了处理 OAuth 流程、HTTP 请求以及 JSON 解析所需的功能。
com.google.api-client google-api-client1.32.1 com.google.oauth-client google-oauth-client1.32.1 com.google.oauth-client google-oauth-client-jetty1.32.1 com.google.http-client google-http-client-jackson21.39.2 com.google.oauth-client google-oauth-client-java61.32.1
请注意,版本号可能需要根据最新的稳定版本进行调整。
出于安全性和最佳实践考虑,您的 clientId 和 clientSecret 不应硬编码在源代码中。Google 推荐将这些凭据存储在一个名为 client_secrets.json 的文件中。
下载的 client_secrets.json 文件通常具有以下结构:
{
"web": { // 如果您选择的是“Web 应用”类型,这里是 "web"
"client_id": "YOUR_CLIENT_ID.apps.googleusercontent.com",
"project_id": "your-project-id",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "YOUR_CLIENT_SECRET",
"redirect_uris": [
"http://localhost" // 或其他配置的重定向 URI
]
},
"installed": { // 如果您选择的是“桌面应用”类型,这里是 "installed"
"client_id": "YOUR_CLIENT_ID.apps.googleusercontent.com",
"project_id": "your-project-id",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_secret": "YOUR_CLIENT_SECRET",
"redirect_uris": [
"http://localhost"
]
}
}请确保您的 client_secrets.json 文件位于项目的类路径下(例如 src/main/resources 目录),以便程序可以加载它。
以下是使用 Java 实现 Google OAuth 2.0 授权代码流的完整代码示例。此示例将引导用户在浏览器中完成认证,然后应用程序将接收授权代码并交换为访问令牌。
package com.example.googleauth; import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.extensions.java6.auth.oauth2.FileDataStoreFactory; import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; importcom.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.HttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.jackson2.JacksonFactory; import com.google.api.client.util.store.DataStoreFactory; import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.security.GeneralSecurityException; import java.util.Collections; import java.util.List; public class GoogleTokenFetcher { /** 应用程序名称,用于标识用户代理 */ private static final String APPLICATION_NAME = "Google Token Fetcher"; /** JSON 工厂,用于解析 JSON 响应 */ private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); /** HTTP 传输器 */ private static HttpTransport HTTP_TRANSPORT; /** 数据存储工厂,用于持久化凭据(如刷新令牌) */ private static FileDataStoreFactory DATA_STORE_FACTORY; /** * 定义应用程序所需的授权范围(Scopes)。 * 这里以访问 Google 日历为例,您可以根据需要更改。 * 更多 Scope 请参考 Google API 文档。 */ private static final List
SCOPES = Collections.singletonList("https://www.googleapis.com/auth/calendar.readonly"); // 如果需要访问用户个人资料,可以使用 "https://www.googleapis.com/auth/userinfo.profile" // 或 "https://www.googleapis.com/auth/userinfo.email" // 甚至更通用的 "https://www.googleapis.com/auth/drive.readonly" 等 static { try { HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport(); // 凭据将存储在用户主目录下的 .store/google_token_fetcher 目录中 DATA_STORE_FACTORY = new FileDataStoreFactory(new File(System.getProperty("user.home"), ".store/google_token_fetcher")); } catch (GeneralSecurityException | IOException e) { e.printStackTrace(); System.exit(1); } } public static void main(String[] args) throws IOException { try { Credential credential = authorize(); if (credential != null && credential.getAccessToken() != null) { String accessToken = credential.getAccessToken(); System.out.println("成功获取访问令牌 (Access Token): " + accessToken); // 您可以使用这个访问令牌调用 Google API // 例如: // Calendar service = new Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY, credential) // .setApplicationName(APPLICATION_NAME) // .build(); // Event event = service.events().get("primary", "eventId").execute(); // System.out.println("获取到的事件:" + event.getSummary()); } else { System.out.println("未能获取访问令牌。"); } } catch (Exception e) { System.err.println("授权过程中发生错误: " + e.getMessage()); e.printStackTrace(); } } /** * 授权安装的应用程序访问用户的受保护数据。 * @return Credential 对象,包含访问令牌和刷新令牌 * @throws Exception 如果授权过程中发生错误 */ private static Credential authorize() throws Exception { // 1. 加载客户端密钥 (client_secrets.json) // 确保 client_secrets.json 文件在类路径中 (例如 src/main/resources) GoogleClientSecrets clientSecrets = GoogleClientSecrets.load( JSON_FACTORY, new InputStreamReader(GoogleTokenFetcher.class.getResourceAsStream("/client_secrets.json")) ); // 2. 设置授权代码流 GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder( HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES // 应用程序所需的权限范围 ) .setDataStoreFactory(DATA_STORE_FACTORY) // 用于持久化凭据,例如刷新令牌 .setAccessType("offline") // 请求刷新令牌,以便在访问令牌过期后重新获取 .build(); // 3. 授权用户 // LocalServerReceiver 会在本地启动一个服务器,监听 Google 授权回调 LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8888).build(); // 可以指定端口 return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user"); } }