2016-11-28 140 views
13

我参与构建了一个内部使用的应用程序,用户可以通过该应用程序上传文件以存储在Google云端硬盘中。由于建议不要将服务帐户用作文件所有者,因此我希望代表系统管理员可访问的指定用户帐户上载应用程序。将Google云端硬盘授权给服务帐户失败

我已经创建了应用程序以及一个服务帐户。有服务帐户创建了两个按键,因为我已经试用过JSON和PKCS12格式努力实现这一点:

API Manager Credentials page

我已经下载了OAuth 2.0用户端ID的详细信息,也有以.json服务帐户键或.p12文件(在按以上顺序显示):

Downloaded files for client and service accounts

我有我的系统经过这里的详细步骤把权力用于服务帐户驱动器的API访问:https://developers.google.com/drive/v2/web/delegation#delegate_domain-wide_authority_to_your_service_account

我们发现在第4步中唯一适用于“客户端名称”的是为Web应用程序列出的“客户端ID”(结束.apps.googleusercontent.com)。为服务帐户键列出的长十六进制的ID是不是需要它(见下文):

Authorized API client access

此前上述,我有代码这将创建一个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; 
} 

,对于上市和上传作品,当然虽然有用于访问文件中没有网络用户界面,它好像它不处理诸如权限元数据或生成缩略图等PDF文件。这就是我试图使用标准帐户进行上传的原因。

一旦代表团显然被排序,我然后尝试调整上面链接的委托引用中显示的代码,并结合其他地方的代码从.json密钥文件中提取必要的详细信息。有了这个代码,只要我努力,以执行任何API命令,即使是简单的:

FileList fileList = service.FileList().Execute(); 

我收到一个错误:

异常详细信息:Google.Apis.Auth.OAuth2.Responses.TokenResponseException :错误:“unauthorized_client”,详细描述:“在请求未授权客户端或范围。”,乌里:“”

该努力的代码是:

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 = "[email protected]" 
    } 
    .FromPrivateKey(privateKey) 
); 

    var service = new DriveService(new BaseClientService.Initializer() 
    { 
    HttpClientInitializer = credential, 
    ApplicationName = "MyAppName" 
    }); 

    return service; 
} 

最后,我创建第二本身rvice帐户密钥保存一个。为了将权限授权文档中的代码,更符合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 = "[email protected]" 
    } 
    .FromCertificate(certificate) 
); 

    var service = new DriveService(new BaseClientService.Initializer() 
    { 
    HttpClientInitializer = credential, 
    ApplicationName = "MyAppName" 
    }); 

    return service; 
} 

的minimial相关类,其中这个方法住是:

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

这可能是一个配置错误的问题。您是否在客户名称中输入了服务帐户的客户ID?它是[委托服务帐户的全域授权]中的第5步(https://developers.google.com/drive/v2/web/delegation#delegate_domain-wide_authority_to_your_service_account) – noogui

+0

@noogui生成的.json文件用于服务帐户密钥包含“client_id”条目,该条目是21位十进制数字。在步骤#5中使用它会显示一条错误消息:“此客户名称尚未在Google上注册。” –

回答

6

我自己找到答案了,它配置,不是代码。我与权限授权步骤共享的链接没有提及当创建服务帐户时可用的选项:表示该帐户将有资格进行域范围委派(DwD)的复选框。

此链接介绍服务帐户的创建和代表团更准确:https://developers.google.com/identity/protocols/OAuth2ServiceAccount

我不知道DWD当我创建的服务帐户,所以我没有选择该选项。可以返回并编辑服务帐户来选择它。一旦我这样做了,我就可以检索到正确的客户端ID,以用于管理控制台的“管理API客户端访问”部分。然后使用GetServiceC()方法按预期工作,并且我可以在同一个应用程序域中为用户检索文件。

这是一个需要被选中为服务帐户的复选框,才有资格对权威的全域代表团:

Creation of a new service account - DwD checkbox

这是可用的额外信息,一旦你做到了这一点(与一次性服务帐户旁边那没有有框打勾,比较):

Comparison of service accounts with and without DwD

+0

我没有在关键用户界面上看到新的域委托选项,并且以前从未需要它 - 必须是一个新的属性。很高兴你把事情解决了! –

-2

大部分一切正常,但:

A.使用ServiceC代码,不知道如果对象打字的问题,但你行:

var credential = new ServiceAccountCredential... 

应该

ServiceAccountCredential credential = new ServiceAccountCredential... 

B.检查ServiceC中的P12文件是您真正上传到您运行此环境的环境中的真实P12文件。 C.使用您用来创建和调用服务的确切可运行代码更新您的问题:filelist:执行代码。这样就会有更多的清晰度和更少的假设。

+0

A.使用'var'将没有区别,请参阅https://msdn.microsoft.com/en-us/library/bb384061.aspx –

+0

(B)我在本地机器上运行它;我澄清了代码以显示我直接从App_Data文件夹加载它。 (C)我已经添加了用于执行FileList命令的实际代码(对所有GetService()方法通用) –

1

您可以勾选复选框在管理面板上创建服务帐户时,启用G Suite域范围委派

Regards

+0

谢谢,但正如你所看到的,我已经设法发现我出错的地方了! :) –

相关问题