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

Vic*_*sey 24 c# google-analytics-api google-oauth google-api-dotnet-client service-accounts

我试图通过简单的服务帐户登录来使用C#,Google API和Google Analytics(分析)来打败自己.我的公司已经将数据导入到Analytics中,我可以使用他们的查询资源管理器查询信息,但是.Net的入门不会随处可见.我正在使用谷歌生成的带有PKI的json文件,因为文档说这样的服务帐户是与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
Run Code Online (Sandbox Code Playgroud)

它编译都很好,但是当它遇到Execute()语句时,GoogleApiException会抛出一个错误:

[凭证无效]位置[授权 - 标题]原因[authError]域[全局]

我错过了什么?

Vic*_*sey 28

似乎GoogleAnalytics不能使用泛型GoogleCredential并将其解释为ServiceAccountCredential(尽管从某种程度上确认它实际上属于该类型).因此,你必须创造一个ServiceAccountCredential艰难的方式.同样不幸的是GoogleCredential,没有暴露证书的各种属性,所以我必须建立自己的.

我在http://jsonclassgenerator.codeplex.com/上使用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();
Run Code Online (Sandbox Code Playgroud)

有些人可能想知道使用PKI(私钥)的Google生成的服务帐户凭据是什么样的.从https://console.developers.google.com/iam-admin/projects上的Google API管理器(IAM和管理员)中,选择相应的项目(至少有一个项目).现在选择服务帐户(从左侧导航链接),并在屏幕顶部创建服务帐户.填写名称,将Furnish设置为新的私钥复选框,然后单击" 创建".Google会自动下载JSON文件,如下所示:

{
  "type": "service_account",
  "project_id": "atomic-acrobat-135",
  "private_key_id": "508d097b0bff9e90b8d545f984888b0ef31",
  "private_key": "-----BEGIN PRIVATE KEY-----\nMIIE...o/0=\n-----END PRIVATE KEY-----\n",
  "client_email": "google-analytics@atomic-acrobat-135.iam.gserviceaccount.com",
  "client_id": "1123573016559832",
  "auth_uri": "https://accounts.google.com/o/oauth2/auth",
  "token_uri": "https://accounts.google.com/o/oauth2/token",
  "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
  "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/google-analytics%40atomic-acrobat-135923.iam.gserviceaccount.com"
}
Run Code Online (Sandbox Code Playgroud)

  • 我懒得用你的班级生成器.所以我这样做了:`Newtonsoft.Json.Linq.JObject cr =(Newtonsoft.Json.Linq.JObject)JsonConvert.DeserializeObject(json); string s =(string)cr.GetValue("private_key");`谢谢你的回答 - 它帮了很多忙. (3认同)

dlu*_*mpp 5

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

A GoogleCredential是不可变的,因此CreateScoped将使用指定的范围集创建一个新实例。

像这样用范围内的结果重新分配您的凭据变量,它应该可以工作:

  if (_cred.IsCreateScopedRequired) {
    _cred = _cred.CreateScoped(AnalyticsService.Scope.Analytics);
  }
Run Code Online (Sandbox Code Playgroud)

可接受的答案之所以有用,是因为它以一种更加困难的方式实现了同一件事。

  • 这对我有所帮助,因为我根本不需要设置范围,因为我不需要python等效项 (2认同)

Mar*_*pez 5

在 2020 年,您不需要执行所有这些操作,并且 GoogleCredential 可以正常工作。除了一行之外,问题中的代码看起来是正确的:

credentials.CreateScoped(new string[] { DriveService.Scope.Drive });
Run Code Online (Sandbox Code Playgroud)

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;
        }
Run Code Online (Sandbox Code Playgroud)


小智 5

对于 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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并查看

@{
    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>
Run Code Online (Sandbox Code Playgroud)