如何使用C#中的服务帐户登录Google API-凭据无效

如何使用C#中的服务帐户登录Google API-凭据无效,c#,google-oauth,google-analytics-api,google-api-dotnet-client,service-accounts,C#,Google Oauth,Google Analytics Api,Google Api Dotnet Client,Service Accounts,我正在拼命尝试获得一个简单的服务账号登录到C#,谷歌API和谷歌分析。我的公司已经将数据输入到分析中,我可以使用他们的查询浏览器查询信息,但在.Net中入门是行不通的。我使用的是谷歌生成的json文件和PKI,因为文档中说这样的服务帐户是使用Googla API进行计算机间通信的正确方式。代码snipet: public static GoogleCredential _cred; public static string _exePath; static void Main(string[]

我正在拼命尝试获得一个简单的服务账号登录到C#,谷歌API和谷歌分析。我的公司已经将数据输入到分析中,我可以使用他们的查询浏览器查询信息,但在.Net中入门是行不通的。我使用的是谷歌生成的json文件和PKI,因为文档中说这样的服务帐户是使用Googla API进行计算机间通信的正确方式。代码snipet:

public static GoogleCredential _cred;
public static string _exePath;

static void Main(string[] args) {
    _exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().GetName().CodeBase).Replace(@"file:\", "");
    var t = Task.Run(() => Run());
    t.Wait();
}

private static async Task Run() {
    try {
        // Get active credential
        using (var stream = new FileStream(_exePath + "\\Default-GASvcAcct-508d097b0bff.json", FileMode.Open, FileAccess.Read)) {
            _cred = GoogleCredential.FromStream(stream);
        }
        if (_cred.IsCreateScopedRequired) {
        _cred.CreateScoped(new string[] { AnalyticsService.Scope.Analytics });
        }
        // Create the service
        AnalyticsService service = new AnalyticsService(
            new BaseClientService.Initializer() {
                HttpClientInitializer = _cred,
            });
        var act1 = service.Management.Accounts.List().Execute(); // blows-up here
这一切都可以正常编译,但当它遇到Execute()语句时,会抛出一个
GoogleApiException
错误:

[无效凭据]位置[授权-标头]原因[授权错误]域[全局]


我遗漏了什么?

谷歌分析公司似乎无法使用通用的
GoogleCredential
,并将其解释为
ServiceAccountCredential
(尽管内部承认它实际上属于该类型)。因此,您必须以艰难的方式创建
ServiceAccountCredential
。同样不幸的是,
GoogleCredential
没有公开凭证的各种属性,因此我不得不构建自己的。

我使用位于的JSON C#类生成器使用JSON库构建了一个“个人”ServiceAccountCredential对象,该库是Google API(Newtonsoft.JSON)的自动部分,检索服务帐户下载的JSON文件的基本部分,使用其电子邮件和私钥属性构造所需的凭据。将正版
ServiceAccountCredential
传递给GoogleAnalytics服务构造函数,将导致成功登录并访问该帐户允许的资源

以下工作代码示例:

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Analytics.v3;
using Newtonsoft.Json;
    .
    .
    .
try
{
    // Get active credential
    string credPath = _exePath + @"\Private-67917519b23f.json";

    var json = File.ReadAllText(credPath);
    var cr = JsonConvert.DeserializeObject<PersonalServiceAccountCred>(json); // "personal" service account credential

    // Create an explicit ServiceAccountCredential credential
    var xCred = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(cr.ClientEmail)
    {
        Scopes = new[] {
            AnalyticsService.Scope.AnalyticsManageUsersReadonly,
            AnalyticsService.Scope.AnalyticsReadonly
        }
    }.FromPrivateKey(cr.PrivateKey));

    // Create the service
    AnalyticsService service = new AnalyticsService(
        new BaseClientService.Initializer()
        {
            HttpClientInitializer = xCred,
        }
    );

    // some calls to Google API
    var act1 = service.Management.Accounts.List().Execute();

    var actSum = service.Management.AccountSummaries.List().Execute();

    var resp1 = service.Management.Profiles.List(actSum.Items[0].Id, actSum.Items[0].WebProperties[0].Id).Execute();

另一个选项是使用
GoogleCredential.GetApplicationDefault()
。我认为这是目前(2018年10月)推荐的方法。这里有一些F#,但在C#模语法中大致相同:

let projectId = "<your Google Cloud project ID...>"
let creds =
  GoogleCredential.GetApplicationDefault()
    .CreateScoped(["https://www.googleapis.com/auth/cloud-platform"])
use service =
  new CloudBuildService(
    BaseClientService.Initializer(HttpClientInitializer=creds))
let foo = service.Projects.Builds.List(projectId).Execute()
let projectId=“”
让信誉=
GoogleCredential.GetApplicationDefault()
.CreateScoped([“https://www.googleapis.com/auth/cloud-platform"])
使用服务=
新的CloudBuildService(
BaseClientService.Initializer(HttpClientInitializer=creds))
让foo=service.Projects.Builds.List(projectd.Execute())

现在,只需确保将
GOOGLE\u APPLICATION\u CREDENTIALS
设置为指向带有凭据JSON文件的文件,例如
GOOGLE\u APPLICATION\u CREDENTIALS=creds.JSON dotnet run

发生无效凭据错误,因为您指定的范围实际上没有随凭据一起发送。我犯了同样的错误,只是在调试后才意识到,在
CreateScoped
调用之后,仍然在凭证上看到0个作用域

GoogleCredential
是不可变的,因此
CreateScoped
使用指定的作用域集创建一个新实例

使用作用域结果重新分配您的credentials变量,如下所示:

  if (_cred.IsCreateScopedRequired) {
    _cred = _cred.CreateScoped(AnalyticsService.Scope.Analytics);
  }

被接受的答案之所以有效,是因为它以一种更为困难的方式实现了同样的目标

到2020年,你不需要做所有这些,谷歌的认证也可以正常工作。除了一行代码外,问题中的代码看起来是正确的:

credentials.CreateScoped(new string[] { DriveService.Scope.Drive });
CreateScoped
方法返回凭证的副本。如果你把它重新分配给它自己,它就会工作

为了完整性起见,这是我的测试代码,它可以完美地工作:

using (var stream =
            new FileStream("drive-credentials.json", FileMode.Open, FileAccess.Read))
        {                
            var credentials = GoogleCredential.FromStream(stream);
            if (credentials.IsCreateScopedRequired)
            {
                credentials = credentials.CreateScoped(new string[] { DriveService.Scope.Drive });
            }


            var service = new DriveService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credentials,
                ApplicationName = "application name",                    
            });

            FilesResource.ListRequest listRequest = service.Files.List();
            listRequest.PageSize = 10;
            listRequest.Fields = "nextPageToken, files(id, name)";

            // List files.
            IList<Google.Apis.Drive.v3.Data.File> files = listRequest.Execute()
                .Files;
        }
使用(var流)=
新文件流(“drive credentials.json”、FileMode.Open、FileAccess.Read))
{                
var-credentials=GoogleCredential.FromStream(流);
如果(需要凭据.IsCreateScopedRequired)
{
credentials=credentials.createScope(新字符串[]{DriveService.Scope.Drive});
}
var service=new DriveService(new BaseClientService.Initializer()
{
HttpClientInitializer=凭证,
ApplicationName=“应用程序名称”,
});
fileResource.ListRequest ListRequest=service.Files.List();
listRequest.PageSize=10;
listRequest.Fields=“nextPageToken,files(id,name)”;
//列出文件。
IList files=listRequest.Execute()
.档案;
}

对于2020年,电话如下:

using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Google.Apis.Services;
using Google.Apis.Auth.OAuth2;
using System.IO;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;

namespace SistemasInfinitos.Controllers.Google.Apis.Sample.MVC4
{
    public class SpreadsheetseController : Controller
    { 
        public ActionResult IndexAPI()
        {
            //accede a las credenciales
            var stream = new FileStream(Server.MapPath("~/quickstart2-9aaf.json"),
                FileMode.Open
               // FileAccess.Read//SOLO LECTURA
                );
            //abre las credenciales
            var credentials = GoogleCredential.FromStream(stream);

            //virifica las credenciales
            if (credentials.IsCreateScopedRequired)
            {
                credentials = credentials.CreateScoped(new string[] { SheetsService.Scope.Spreadsheets });
            }
            ///inicializa la api
        var service = new SheetsService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credentials,
                ApplicationName = "SistemasInfinitos",
            });

            // Define los parametros.  
            String spreadsheetId = "1MKxeqXV5UEMXU2yBe_xi0nwjooLhNN6Vk";
            String range = "Sheet1";
            SpreadsheetsResource.ValuesResource.GetRequest request =service.Spreadsheets.Values.Get(spreadsheetId, range);
            // imprime   
            ValueRange response = request.Execute();
            IList<IList<Object>> values = response.Values;
            ViewBag.List = values;
            return View();
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Web.Mvc;
使用Google.api.Services;
使用Google.api.Auth.OAuth2;
使用System.IO;
使用Google.api.Sheets.v4;
使用Google.api.Sheets.v4.Data;
命名空间SistemasInfinitos.Controllers.Google.api.Sample.MVC4
{
公共类电子表格控制器:控制器
{ 
公共行动结果索引()
{
//加入一个拉斯克雷登夏莱斯
var stream=new FileStream(Server.MapPath(“~/quickstart2-9aaf.json”),
文件模式。打开
//FileAccess.Read//a
);
//阿布雷·拉斯克雷登夏莱斯酒店
var-credentials=GoogleCredential.FromStream(流);
//克雷登西亚病毒
如果(需要凭据.IsCreateScopedRequired)
{
credentials=credentials.CreateScoped(新字符串[]{SheetsService.Scope.Spreadsheets});
}
///阿皮亚酒店
var service=new SheetsService(new BaseClientService.Initializer()
{
HttpClientInitializer=凭证,
ApplicationName=“SistemasInfinitos”,
});
//定义服务水平参数。
字符串电子表格ID=“1MKxeqXV5UEMXU2yBe_xi0nwjooLhNN6Vk”;
字符串范围=“Sheet1”;
电子表格sresource.ValuesResource.GetRequest request=service.Spreadsheets.Values.Get(电子表格ID,范围);
//
using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Google.Apis.Services;
using Google.Apis.Auth.OAuth2;
using System.IO;
using Google.Apis.Sheets.v4;
using Google.Apis.Sheets.v4.Data;

namespace SistemasInfinitos.Controllers.Google.Apis.Sample.MVC4
{
    public class SpreadsheetseController : Controller
    { 
        public ActionResult IndexAPI()
        {
            //accede a las credenciales
            var stream = new FileStream(Server.MapPath("~/quickstart2-9aaf.json"),
                FileMode.Open
               // FileAccess.Read//SOLO LECTURA
                );
            //abre las credenciales
            var credentials = GoogleCredential.FromStream(stream);

            //virifica las credenciales
            if (credentials.IsCreateScopedRequired)
            {
                credentials = credentials.CreateScoped(new string[] { SheetsService.Scope.Spreadsheets });
            }
            ///inicializa la api
        var service = new SheetsService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credentials,
                ApplicationName = "SistemasInfinitos",
            });

            // Define los parametros.  
            String spreadsheetId = "1MKxeqXV5UEMXU2yBe_xi0nwjooLhNN6Vk";
            String range = "Sheet1";
            SpreadsheetsResource.ValuesResource.GetRequest request =service.Spreadsheets.Values.Get(spreadsheetId, range);
            // imprime   
            ValueRange response = request.Execute();
            IList<IList<Object>> values = response.Values;
            ViewBag.List = values;
            return View();
        }
    }
}
@{
    ViewBag.Title = "IndexAPI";
}

<div class="col-md-6">
    <h3>Read Data From Google Live sheet</h3>
    <table class="table" id="customers">
        <thead>
            <tr>
                <th>
                    id
                </th>
                <th>
                    Name
                </th>
            </tr>
        </thead>
        <tbody>
            @{
                foreach (var item in ViewBag.List)
                {
                    <tr>
                        <td>@item[0]</td>
                        <td>@item[1]</td>
                    </tr>

                }
            }
        </tbody>

    </table>
</div>