代码之家  ›  专栏  ›  技术社区  ›  Vic Lindsey

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

  •  33
  • Vic Lindsey  · 技术社区  · 10 年前

    为了让一个简单的服务帐户登录在C#、GoogleAPI和GoogleAnalytics中工作,我拼命地折磨自己。我的公司已经在将数据导入Analytics,我可以使用他们的query Explorer查询信息,但从.Net开始是行不通的。我在PKI中使用Google生成的json文件,因为文档说明这样的服务帐户是使用Googla API进行计算机间通信的正确方式。代码截图:

    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 引发错误:

    [无效凭据]位置[Authorization-header]原因[authError]域[global]

    我错过了什么?

    6 回复  |  直到 10 年前
        1
  •  42
  •   Vic Lindsey    10 年前

    GoogleCredential 并将其解释为 ServiceAccountCredential (尽管内部承认,事实上 属于那种类型 服务帐户凭据 艰难的道路。不幸的是 谷歌凭证

    我在以下位置使用了JSON C#类生成器 http://jsonclassgenerator.codeplex.com/ 使用JSON库构建“个人”ServiceAccountCredential对象,该库是Google API(Newtonsoft.JSON)的自动部分,检索服务帐户下载的JSON文件的重要部分,使用其电子邮件和私钥属性构造所需的凭据。通过一个真正的 服务帐户凭据

    工作代码示例如下:

    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();
    

    https://console.developers.google.com/iam-admin/projects ,选择适当的项目(您至少有一个项目)。现在选择 服务帐户 (从左侧导航链接),以及 创建服务帐户 屏幕顶部。填写名称,设置 提供新的私钥 复选框,然后单击 创造 .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"
    }
    
        2
  •  8
  •   dlumpp    7 年前

    发生无效凭据错误,因为您指定的范围实际上没有与凭据一起发送。我犯了同样的错误,只有在调试后才意识到,在执行 CreateScoped 呼叫

    A. GoogleCredential 是不可变的,所以 创建作用域 创建具有指定范围集的新实例。

    将凭证变量重新分配给类似这样的范围结果,它应该可以工作:

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

    公认的答案之所以有效,是因为它以更困难的方式实现了同样的目标。

        3
  •  5
  •   Mario Lopez    6 年前

    到2020年,你不需要做所有这些,GoogleCredential运行良好。问题中的代码看起来正确,只有一行除外:

    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;
            }
    
        4
  •  5
  •   user10304366 user10304366    6 年前

    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();
            }
        }
    }
    

    和视图

    @{
        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>
    
        5
  •  1
  •   skainswo    8 年前

    另一种选择是使用 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()
    

    现在,只要确保设置 GOOGLE_APPLICATION_CREDENTIALS 指向带有凭据JSON文件的文件,例如。 GOOGLE_APPLICATION_CREDENTIALS=creds.json dotnet run .

        6
  •  0
  •   derekbaker783 Pxtl    5 年前

    如果您在试图确定如何创建 ServiceAccountCredential ,如果不直接使用密钥文件,您可能会想知道以下方法是否有效(有时):

    GoogleCredential credential = GoogleCredential.GetApplicationDefault();
    
    ServiceAccountCredential serviceAccountCredential = 
        credential.UnderlyingCredential as ServiceAccountCredential;