如何从服务帐户密钥文件创建凭据?

How to create Credential from service account key file?

我正在做一个自动生成文件的服务,并使用 Spring 引导和 java 8 将其上传到 google 驱动器。这是另一个更大服务的外部服务。

我找不到任何关于使用 Google 驱动器 API 的服务帐户密钥创建凭据的说明,所以我根据以下说明自己编写了这段代码:https://cloud.google.com/docs/authentication/production。该代码基本上执行一个简单的 GET 请求以获取我的 Google 驱动器中的所有文件和文件夹。它 运行 但出现异常

这是代码

package com.example.demo.controller;

import com.example.demo.dto.FileItemDTO;
import com.example.demo.service.GoogleDriveService;
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.http.HttpTransport;
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.client.util.store.FileDataStoreFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@RestController
public class GoogleDriveController {
    @Value("${google.service.account.key}")
    private static Resource serviceAccountKey;

    @Value("${external.address}")
    private String externalAddress;

    private final GoogleDriveService googleDriveService;
    private static HttpTransport HTTP_TRANSPORT = new NetHttpTransport();
    private static JsonFactory JSON_FACTORY = GsonFactory.getDefaultInstance();
    private static final List<String> SCOPES = Collections.singletonList(DriveScopes.DRIVE_METADATA_READONLY);
    private static final String TOKENS_DIRECTORY_PATH = "tokens";

    @Autowired
    public GoogleDriveController(GoogleDriveService googleDriveService) throws IOException {
        this.googleDriveService = googleDriveService;
    }

    private static Credential getCredentials(final NetHttpTransport HTTP_TRANSPORT) throws IOException {
        // Load client secrets.
        java.io.File file = new java.io.File("path-to-credential-file");
        InputStream inputStream = new FileInputStream(file);
        GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(inputStream));

        // Build flow and trigger user authorization request.
        GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
                .setDataStoreFactory(new FileDataStoreFactory(new java.io.File(TOKENS_DIRECTORY_PATH)))
                .setAccessType("offline")
                .build();
        LocalServerReceiver receiver = new LocalServerReceiver.Builder().setPort(8868).build();

        return new AuthorizationCodeInstalledApp(flow, receiver).authorize("user");
    }

    @GetMapping(value = {"/static-page/get"})
    public @ResponseBody
    List<FileItemDTO> getStaticPage() throws Exception {
        String pageToken = null;

        final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();

        Drive drive = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, getCredentials(HTTP_TRANSPORT)).build();
        List<FileItemDTO> responseList = new ArrayList<>();

        FileList fileList = drive.files().list().setFields("files(id,name,thumbnailLink)").execute();
        for (File file : fileList.getFiles()) {
            FileItemDTO item = new FileItemDTO();
            item.setId(file.getId());
            item.setName(file.getName());
            item.setThumbnailLink(file.getThumbnailLink());
            responseList.add(item);
        }

        return responseList;
    }
}

服务帐户获得所有者角色。有人可以帮我解决这个异常吗?

据我了解,具有所有者角色的服务帐户密钥具有最高权限。

编辑: 按照@DalmTo 回答后,我删除了 getCredential() 方法,创建 Drive 对象的代码变成了这样,现在一切正常:

// file 1 is the JSON credential file
// GoogleCredential have been deprecated so instead I use HttpRequestInitializer 
HttpRequestInitializer requestInitializer = new HttpCredentialsAdapter(ServiceAccountCredentials.fromStream(new FileInputStream(file1))
        .createScoped(DriveScopes.all()));
Drive drive = new Drive.Builder(HTTP_TRANSPORT, JSON_FACTORY, requestInitializer)
        .setApplicationName(ApplicationName).build();

您使用的代码专为与已安装的应用程序一起使用而设计,因此是 AuthorizationCodeInstalledApp。您不能通过服务帐户使用已安装应用程序的代码。

    HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
    GoogleCredential credential = GoogleCredential
        .fromStream(new FileInputStream(KEY_FILE_LOCATION))
        .createScoped(DriveScopes.all());

    // Construct the drive service object.
    return new Drive.Builder(httpTransport, JSON_FACTORY, credential)
        .setApplicationName(APPLICATION_NAME).build();
  }

The code basically perform a simple GET request to get all file and folder in my Google drive. It run but got the exception

请记住,服务帐户不是您,它是虚拟用户,它有自己的 google 驱动器帐户。您可以与服务帐户共享个人 google 驱动器帐户上的文件夹,并授予其访问此文件夹的权限。您不能与它共享根文件夹,您必须自己与它共享所有内容。