Java使用服务账号认证Google Sheets API报错解决方案
问题根因
官方Java版快速入门的示例代码是为个人用户OAuth授权流程设计的,仅能解析OAuth客户端ID导出的凭证文件,和服务账号导出的JSON凭证结构完全不匹配。你遇到的IllegalArgumentException是代码尝试将服务账号凭证按OAuth客户端密钥格式解析时,字段校验失败直接抛出的,和表格权限、spreadsheetId配置无关——你之前做的这两项配置是正确的,问题核心是认证逻辑没有匹配服务账号的认证模式。
解决方案
依赖配置
首先确保引入最新版的官方认证依赖,不要使用已废弃的旧版认证类:
// Gradle 依赖示例 implementation 'com.google.auth:google-auth-library-oauth2-http:1.23.0' implementation 'com.google.apis:google-api-services-sheets:v4-rev20240326-2.0.0' implementation 'com.google.api-client:google-api-client:2.6.0' implementation 'com.google.http-client:google-http-client-gson:1.44.1'
Maven用户可对应查找上述组件的最新稳定版坐标引入即可。
修正后的完整代码
直接替换原有类的全部逻辑,移除所有OAuth用户授权相关的代码(本地端口监听、本地token存储等逻辑对服务账号场景完全无用):
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; import com.google.api.client.http.javanet.NetHttpTransport; import com.google.api.client.json.JsonFactory; import com.google.api.client.json.gson.GsonFactory; import com.google.api.services.sheets.v4.Sheets; import com.google.api.services.sheets.v4.SheetsScopes; import com.google.api.services.sheets.v4.model.ValueRange; import com.google.auth.http.HttpCredentialsAdapter; import com.google.auth.oauth2.GoogleCredentials; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.security.GeneralSecurityException; import java.util.Collections; import java.util.List; public class SheetsQuickstart { private static final String APPLICATION_NAME = "Google Sheets API Service Account Demo"; private static final JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance(); private static final String CREDENTIALS_FILE_PATH = "/credentials.json"; // 按需调整权限范围,写操作替换为SheetsScopes.SPREADSHEETS即可 private static final List<String> SCOPES = Collections.singletonList(SheetsScopes.SPREADSHEETS_READONLY); private static GoogleCredentials getServiceAccountCredentials() throws IOException { InputStream credentialStream = SheetsQuickstart.class.getResourceAsStream(CREDENTIALS_FILE_PATH); if (credentialStream == null) { throw new FileNotFoundException("Service account credential file missing: " + CREDENTIALS_FILE_PATH); } return GoogleCredentials.fromStream(credentialStream).createScoped(SCOPES); } public static void main(String... args) throws IOException, GeneralSecurityException { final NetHttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport(); final String spreadsheetId = "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"; final String range = "Class Data!A2:E"; Sheets sheetsService = new Sheets.Builder( httpTransport, JSON_FACTORY, // 用官方提供的适配器包装服务账号凭证 new HttpCredentialsAdapter(getServiceAccountCredentials()) ).setApplicationName(APPLICATION_NAME).build(); ValueRange response = sheetsService.spreadsheets().values() .get(spreadsheetId, range) .execute(); List<List<Object>> values = response.getValues(); if (values == null || values.isEmpty()) { System.out.println("No data found."); return; } System.out.println("Name, Major"); for (List<Object> row : values) { System.out.printf("%s, %s\n", row.get(0), row.get(4)); } } }
注意事项
- 服务账号属于服务到服务的认证模式,不需要弹出浏览器让用户授权,也不需要本地监听8888端口、不需要在本地存储token缓存,原有代码里的相关逻辑可以全部删除。
- 仅设置表格“持有链接的用户可编辑”对服务账号不生效,必须单独把目标表格的对应权限(读/写)授予服务账号JSON文件中
client_email字段对应的邮箱地址,这一步你之前已经配置完成,不需要重复调整。 - 上述代码全部使用当前官方推荐的非废弃API,没有使用已淘汰的
GoogleCredential类,后续版本升级兼容性有保障。 - 如果需要切换权限范围(比如从只读改为读写),直接修改
SCOPES列表的取值即可,不需要重新下载服务账号凭证。
内容的提问来源于stack exchange,提问作者Piyush Soni
相关产品推荐
相关产品推荐

