我参与构建内部使用应用程序,用户可以通过该应用程序上传文件,以存储在Google云端硬盘中。由于建议不要将服务帐户用作文件所有者,我希望代表公司系统管理员可以访问的指定用户帐户上传应用程序。
我已经创建了应用程序以及服务帐户。为服务帐户创建了两个密钥,因为我尝试了尝试实现此目的的JSON和PKCS12格式:
我已下载了OAuth 2.0客户端ID详细信息,并且还有服务帐户密钥的.json和.p12文件(按上面显示的顺序):
我让我的系统管理员完成了此处详细说明的步骤,将Drive API访问权限委派给服务帐户:https://developers.google.com/drive/v2/web/delegation#delegate_domain-wide_authority_to_your_service_account
我们发现唯一适用于"客户名称"在第4步中是"客户端ID"列出的Web应用程序(结束.apps.googleusercontent.com)。为服务帐户密钥列出的长十六进制ID不是它所需的(见下文):
在上面的内容之前,我有一些代码可以创建一个可以直接上传到服务帐户的DriveService实例,引用服务帐户密钥的.json文件:
private DriveService GetServiceA()
{
var settings = SettingsProvider.GetInstance();
string keyFilePath = HostingEnvironment.MapPath("~/App_Data/keyfile.json");
var scopes = new string[] { DriveService.Scope.Drive };
var stream = new IO.FileStream(keyFilePath, IO.FileMode.Open, IO.FileAccess.Read);
var credential = GoogleCredential.FromStream(stream);
credential = credential.CreateScoped(scopes);
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "MyAppName"
});
return service;
}
这适用于列表和上传,但当然没有用于访问文件的Web UI,并且似乎它没有处理权限元数据或缩略图生成等内容。 PDF文件。这就是我尝试使用标准帐户进行上传的原因。
一旦委托显然已经排序,我就尝试调整上面链接的委托引用中显示的代码,并结合来自其他地方的代码从.json密钥文件中提取必要的细节。使用此代码,只要我尝试执行任何API命令,即使如此简单:
FileList fileList = service.FileList().Execute();
我收到错误:
异常详细信息:Google.Apis.Auth.OAuth2.Responses.TokenResponseException:错误:" unauthorized_client",说明:"请求中未经授权的客户端或范围。",Uri:& #34;"
该努力的代码是:
private DriveService GetServiceB()
{
var settings = SettingsProvider.GetInstance();
string keyFilePath = HostingEnvironment.MapPath("~/App_Data/keyfile.json");
string serviceAccountEmail = "<account-email>@<project-id>.iam.gserviceaccount.com";
var scopes = new string[] { DriveService.Scope.Drive };
var stream = new IO.FileStream(keyFilePath, IO.FileMode.Open, IO.FileAccess.Read);
var reader = new IO.StreamReader(stream);
string jsonCreds = reader.ReadToEnd();
var o = JObject.Parse(jsonCreds);
string privateKey = o["private_key"].ToString();
var credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes,
User = "designated.user@sameappsdomain.com"
}
.FromPrivateKey(privateKey)
);
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "MyAppName"
});
return service;
}
最后,我创建了第二个服务帐户密钥来保存.p12文件,以便更紧密地匹配权限委派文档中的代码,但这导致了相同的异常:
private DriveService GetServiceC()
{
var settings = SettingsProvider.GetInstance();
string p12KeyFilePath = HostingEnvironment.MapPath("~/App_Data/keyfile.p12");
string serviceAccountEmail = "<account-email>@<project-id>.iam.gserviceaccount.com";
var scopes = new string[] { DriveService.Scope.Drive }; // Full access
X509Certificate2 certificate = new X509Certificate2(
p12KeyFilePath,
"notasecret",
X509KeyStorageFlags.Exportable
);
var credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = scopes,
User = "designated.user@sameappsdomain.com"
}
.FromCertificate(certificate)
);
var service = new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "MyAppName"
});
return service;
}
此方法所在的最小相关类别是:
public class GoogleDrive
{
public DriveService Service { get; private set; }
public GoogleDrive()
{
this.Service = this.GetService();
}
private DriveService GetService()
{
// Code from either A, B or C
}
public FilesResource.ListRequest FileList()
{
return this.Service.Files.List();
}
}
以这种方式使用:
var service = new GoogleDrive();
FilesResource.ListRequest listRequest = service.FileList();
FileList fileList = listRequest.Execute();
最后一行发生异常。
我不明白为什么我的服务帐户不能代表指定用户行事,指定用户是应用程序服务帐户应具有委派权限的域的一部分。我在这里误解了什么?
答案 0 :(得分:7)
我自己找到了答案,是配置,而不是代码。我与权限委派步骤共享的链接未提及创建服务帐户时可用的选项:一个复选框,表明该帐户将有资格进行域范围授权(DwD)。
此链接更准确地描述了服务帐户的创建和委派:https://developers.google.com/identity/protocols/OAuth2ServiceAccount
我在创建服务帐户时不知道DwD,因此我没有选择该选项。可以返回并编辑服务帐户以进行选择。完成此操作后,我能够检索正确的客户端ID,以便在管理控制台的“管理API客户端访问”部分中使用。然后使用GetServiceC()方法按预期工作,我能够为同一Apps域中的用户检索文件。
这是一个复选框,需要勾选一个服务帐户才有资格进行域范围的授权:
这是您完成后可获得的额外信息(除了不之前的一次性服务帐户,方框已打勾,以进行比较):
答案 1 :(得分:1)
在管理面板上创建服务帐户时,您可以勾选启用G Suite全域委派复选框。
此致
答案 2 :(得分:-2)
大多数一切看起来都不错,但是:
一个。使用ServiceC代码,不确定对象输入是否重要但是您的行:
var credential = new ServiceAccountCredential...
应该是
ServiceAccountCredential credential = new ServiceAccountCredential...
B中。检查ServiceC中的P12文件是否是您实际上传到运行此环境的环境中的真实P12文件。
℃。使用您用于创建和调用服务的确切可运行代码更新您的问题:filelist:execute code。通过这种方式,可以获得更清晰,更少的假设。