16

简短的问题是这是否可能,如果可能,如何?

大纲

我有一个 .NET 应用程序,它当前使用服务帐户通过 Google Drive API 访问 Google Apps 域中的信息。这可以很好地使用google-api-dotnet-client 库和代码,与此处示例中所示的相同行- 这是目前我正在做的一个非常好的基本示例。

我现在想做的是扩展它,以及使用“新”google-api-dotnet-client 库提供的那些 API,它使用旧的“GData”库,如旧google-gdata 库提供的那样,特别是电子表格 API(也许还会有更多)。

问题

这就是困难出现的地方。正如上面第一段中的第二个链接所证明的那样,以前的图书馆完全符合我的要求 - 以及我自己做的事实。但是......虽然第二个库已更新为支持 OAuth 2.0 以及 OAuth 1.0 和其他较旧的身份验证技术,但它不允许 - 据我从广泛的谷歌搜索和跟踪错误中得知 - 允许“代表我所有用户的服务帐户”我需要的操作。

我的问题是我是否遗漏了一些可以让我做我想做的事情的东西(可能是很难找到或没有记录的东西)。如果做不到这一点,有什么办法可以强制这种行为并使这两个库并排运行?

理想的解决方案

理想情况下,我希望通过某种方式让Google.GData.Spreadsheets.SpreadsheetsService实例能够利用Google.Apis.Authentication.Auth2Authenticator<AssertionFlowClient>我已经在使用的实例......不知何故。这样的巫术是可能的吗?我错过了显而易见的事情吗?

如果做不到这一点,我很乐意以旧库可以处理的某种方式再次完成整个 OAuth2“断言流客户端”的舞蹈。

帮助?

其他想法

我已经考虑过——并且暂时拒绝了——从头开始并编写自己的库来实现这一点的选择。这有两个原因:

  1. gdata 库已经存在,并且由许多可能比我聪明的人开发。我没有那么自大,我相信我可以做得更好。
  2. 我不确定这些旧 API 是否支持/允许使用带有服务帐户的 OAuth2 方法。

我一直希望避免但可能不得不根据这里的答案回退的另一种方法是使用 2-legged OAuth 1.0 作为其中的一部分。我不希望这样做,因为应用程序的某些部分依赖于一种旧的身份验证方法,而其他部分则以一种不错的新方法来做,这对我来说是错误的。还有更多的问题......


更新

我已经考虑了将 GDataRequestFactory 和 GDataRequest 子类化的可能性,这样我就可以创建自己的请求工厂,并使用Google.Apis.Authentication.Auth2Authenticator<AssertionFlowClient>(好吧,Google.Apis.Authentication.IAuthenticator无论如何的实例)的实例,它可以在请求被调用之前介入以验证请求。但是...... GDataRequest 的构造函数是内部的,这阻止了我。

看起来这不是故意的。

4

3 回答 3

20

为了其他人遇到这个问题(现在在接受的答案中链接到的解决方案使用了不推荐使用的代码),这是我解决它的方法:

首先,Google.Apis.Auth通过设置ServiceAccountCredential以下 Google 的服务帐户示例,在“新 API”领域(使用 nuget 包)开始:

//In the old api, this accessed the main api accounts' sheets, not anymore
//** Important ** share spreadsheets with the Service Account by inviting the "serviceAccountEmail" address to the sheet
string serviceAccountEmail = "12345697-abcdefghijklmnop@developer.gserviceaccount.com";

var certificate = new X509Certificate2(@"key.p12", "notasecret", X509KeyStorageFlags.Exportable);

ServiceAccountCredential credential = new ServiceAccountCredential(
   new ServiceAccountCredential.Initializer(serviceAccountEmail)
   {
       Scopes = new[] { "https://spreadsheets.google.com/feeds", "https://docs.google.com/feeds" }
   }.FromCertificate(certificate));

告诉凭证请求访问令牌:

credential.RequestAccessTokenAsync(System.Threading.CancellationToken.None).Wait();

现在是时候切换回“旧 API”领域了(使用Google.GData.Spreadsheetsnuget 包)。首先构建SpreadsheetsService(类似于 Google 的示例):

SpreadsheetsService service = new SpreadsheetsService("MySpreadsheetIntegration-v1");

要使用服务帐户身份验证,我们将创建一个实例GDataRequestFactory并设置一个自定义Authorization标头:

var requestFactory = new GDataRequestFactory("My App User Agent");
requestFactory.CustomHeaders.Add(string.Format("Authorization: Bearer {0}", credential.Token.AccessToken));

最后,将SpreadsheetsService'RequestFactory属性设置为这个新工厂:

service.RequestFactory = requestFactory;

继续使用,SpreadsheetsService就像您使用任何其他技术进行身份验证一样。(提示:通过邀请serviceAccountEmail地址到工作表与服务帐户共享电子表格)

于 2014-10-08T14:27:50.153 回答
4

我设法通过继承 GDataRequestFactory 并创建自己的 GDataRequest 实现的接口实现来解决这个问题。这个实现包装了一个通过反射实例化的 GDataRequest 实例,并添加了必要的代码以使用 IAuthenticator 实例(在我的例子中为 Auth2Authenticator)执行身份验证。

我在上面写了一篇博文,并添加了一个示例作为要点:

如果它对您有帮助,请随意使用它(BSD 许可证)。

于 2012-12-09T13:28:39.560 回答
3

嘿只是偶然发现了同样的问题并产生了不同的解决方案:

有没有人考虑过将凭证对象中的参数直接写入 OAuth2Parameters 对象?

我这样做了,效果很好:

public class OAuthTest
{  
    OAuth2Parameters param = new OAuth2Parameters();

    public OAuthTest()
    {
        Debug.WriteLine("Calling: AuthGoogleDataInterface()");
        bool init = AuthGoogleDataInterface();
        if (init)
        {
            GOAuth2RequestFactory requestFactory = new GOAuth2RequestFactory(null, "My App User Agent", this.param);
            //requestFactory.CustomHeaders.Add(string.Format("Authorization: Bearer {0}", credential.Token.AccessToken));
            var service = new SpreadsheetsService("MyService");
            service.RequestFactory = requestFactory;
            SpreadsheetQuery query = new SpreadsheetQuery();

            // Make a request to the API and get all spreadsheets.
            SpreadsheetFeed feed = service.Query(query);

            // Iterate through all of the spreadsheets returned
            foreach (SpreadsheetEntry entry in feed.Entries)
            {
                // Print the title of this spreadsheet to the screen
                Debug.WriteLine(entry.Title.Text);
            }
        }
        Debug.WriteLine(m_Init);
    }

    private bool AuthGoogleDataInterface()
    {
        bool b_success;
        try
        {
            Console.WriteLine("New User Credential");
            // New User Credential
            UserCredential credential;
            using (var stream = new FileStream("client_secrets.json", FileMode.Open, FileAccess.Read))
            {
                GoogleClientSecrets GCSecrets = GoogleClientSecrets.Load(stream);
                string[] ArrScope = new[] { "https://spreadsheets.google.com/feeds", "https://docs.google.com/feeds" };
                credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GCSecrets.Secrets,
                    ArrScope,
                    "user", CancellationToken.None,
                new FileDataStore("My.cal")).Result;
                // put the Information generated for the credentials object into the OAuth2Parameters-Object to access the Spreadsheets
                this.param.ClientId = GCSecrets.Secrets.ClientId; //CLIENT_ID;
                this.param.ClientSecret = GCSecrets.Secrets.ClientSecret; //CLIENT_SECRET;
                this.param.RedirectUri = "urn:ietf:wg:oauth:2.0:oob"; //REDIRECT_URI;
                this.param.Scope = ArrScope.ToString();
                this.param.AccessToken = credential.Token.AccessToken;
                this.param.RefreshToken = credential.Token.RefreshToken;
            }

            Debug.WriteLine("AuthGoogleDataInterface: Success");
            b_success = true;
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.ToString());
            b_success = false;
        }
        return b_success;
    }
}
于 2015-01-15T13:30:11.317 回答