2016-11-28 202 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

謝謝,但正如你所看到的,我已經設法發現我出錯的地方了! :) –

相關問題