我在Google App Engine上有一个应用程序(托管在appspot.com上),一些用户将使用它来从互联网上获取一些内容,将其保存在数据存储区中,然后将其写入Google电子表格.我想使用服务帐户访问电子表格但是当我尝试执行任何操作时遇到400 OK {“error”:“invalid_grant”}问题,无论我是否尝试使用Drive API创建文件或使用Spreadsheets API访问现有文件.
我已在API控制台中启用了Drive API和Drive SDK,并生成了P12密钥.这是我构建GoogleCredential的方法:
private GoogleCredential getGoogleCredential() throws IOException, GeneralSecurityException {
GoogleCredential credential = new GoogleCredential.Builder()
.setTransport(TRANSPORT)
.setJsonFactory(FACTORY)
.setServiceAccountId("1511XXX90247.apps.googleusercontent.com")
.setServiceAccountScopes(scopes)
.setServiceAccountPrivateKeyFromP12File(
new File("05a605b0fXXXd2a6be864be15d81a2bd629d3bd6-privatekey.p12"))
.setServiceAccountUser("XXX@gmail.com") // My personal e-mail address which I used to create the project
.build();
return credential;
}
ServiceAccountID来自API控制台.我尝试使用1511XXX90247@developer.gserviceaccount.com作为我的ServiceAccountUser以及myappname@appspot.gserviceaccount.com.我正在使用的范围如下:
https://spreadsheets.google.com/feeds
https://docs.google.com/feeds
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/drive.file
以下是我尝试使用Spreadsheets API时失败的代码:
SpreadsheetService service = new SpreadsheetService("name of my app");
service.setOAuth2Credentials(getGoogleCredential());
FeedURLFactory factory = FeedURLFactory.getDefault();
SpreadsheetQuery query = new SpreadsheetQuery(factory.getSpreadsheetsFeedUrl());
query.setTitleQuery("test");
SpreadsheetFeed feed = service.query(query, SpreadsheetFeed.class);
当服务尝试执行查询时,代码失败.
这是我尝试过的驱动程序代码也失败了:
Drive drive = new Drive.Builder(TRANSPORT, FACTORY,
getGoogleCredential()).build();
com.google.api.services.drive.model.File file = new com.google.api.services.drive.model.File();
file.setTitle("test");
file.setMimeType("application/vnd.google-apps.spreadsheet");
drive.files().insert(file).execute(); // This is where the code fails
无论我尝试什么,我似乎总是得到相同的“无效授权”错误.这是部分堆栈跟踪:
com.google.gdata.util.AuthenticationException: Failed to refresh access token: 400 OK { "error" : "invalid_grant" }
at com.google.gdata.client.GoogleAuthTokenFactory$OAuth2Token.refreshToken(GoogleAuthTokenFactory.java:260)
at com.google.gdata.client.GoogleAuthTokenFactory.handleSessionExpiredException(GoogleAuthTokenFactory.java:702)
at com.google.gdata.client.GoogleService.handleSessionExpiredException(GoogleService.java:738) at com.google.gdata.client.GoogleService.getFeed(GoogleService.java:680)
at com.google.gdata.client.Service.query(Service.java:1237)
at com.google.gdata.client.Service.query(Service.java:1178)
at spam.gwt.scraper.server.spreadsheets.APIConnector.doGet(APIConnector.java:66)
我试图在各处寻找答案,包括但不限于Google Developers Spreadsheets Guide,这Service Account-related Stack Overflow question和Google App Engine-specific Stack Overflow question.随着发展的进展(例如Docs – > Drive),很难找到最新的信息,没关系与GAE有关的信息.
最佳答案 我使用App Engine应用程序的内置服务帐户,而不是创建自己的;这样私钥就是保存(不是本地存储,只存储在app引擎上).
对于驱动器:
protected Drive createDriveService() throws BookingSheetException {
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
GoogleClientRequestInitializer keyInitializer =
new CommonGoogleClientRequestInitializer(API_KEY);
AppIdentityCredential credential =
new AppIdentityCredential.Builder(Arrays.asList(DriveScopes.DRIVE)).build();
Drive service = new Drive.Builder(httpTransport, jsonFactory, null)
.setHttpRequestInitializer(credential)
.setGoogleClientRequestInitializer(keyInitializer)
.setApplicationName(appname).build();
return service;
}
对于电子表格:
private Credential creds;
/**
* Keep the expiration time of the access token to renew it before expiry
*/
private Calendar expirationTime = Calendar.getInstance();
protected void initCredentials() {
List<String> scopes = Arrays.asList("https://spreadsheets.google.com/feeds");
AppIdentityService appIdentity = AppIdentityServiceFactory.getAppIdentityService();
AppIdentityService.GetAccessTokenResult accessToken = appIdentity.getAccessToken(scopes);
expirationTime.setTime(accessToken.getExpirationTime());
expirationTime.add(Calendar.MINUTE,-REFRESH_MINUTES); //refresh some time before expiry
creds = new Credential(BearerToken.authorizationHeaderAccessMethod());
creds.setAccessToken(accessToken.getAccessToken());
}
public SpreadsheetUtil(String appname) {
myService = new SpreadsheetService(appname);
myService.setOAuth2Credentials(cred);
}
确保creds.refreshToken()或在过期时重新初始化.