我正在喋喋不休地试图通过简单的服务帐户登录来使用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
这一切都很好编译,但当它遇到Execute()语句时,会抛出GoogleApiException
错误:
[无效凭据]位置[授权 - 标题]原因[authError]域[全局]
我缺少什么?
答案 0 :(得分:24)
GoogleAnalytics似乎无法使用通用GoogleCredential
并将其解释为ServiceAccountCredential
(即使从某种程度上确认它实际上是该类型的 )。因此,您必须以艰难的方式创建ServiceAccountCredential
。同样令人遗憾的是GoogleCredential
没有公开证书的各种属性,所以我必须建立自己的属性。
我在http://jsonclassgenerator.codeplex.com/使用了JSON C#类生成器来构建一个"个人" ServiceAccountCredential对象使用JSON库作为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();
有些人可能想知道使用PKI(私钥)的Google生成的服务帐户凭据是什么样的。在https://console.developers.google.com/iam-admin/projects的Google API管理器(IAM和管理员)中,选择相应的项目(至少有一个项目)。现在选择服务帐户(来自左侧导航链接),并在屏幕顶部选择创建服务帐户。填写姓名,设置提供新的私钥复选框,然后点击创建。 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"
}
答案 1 :(得分:2)
在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;
}
答案 2 :(得分:2)
对于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>
答案 3 :(得分:0)
另一种选择是使用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
。
答案 4 :(得分:0)
发生无效的凭证错误,因为您指定的作用域实际上并未与您的凭证一起发送。我犯了同样的错误,只有在调试后才意识到,并且在CreateScoped
调用之后仍然看到0范围的凭据。
GoogleCredential
是不可变的,因此CreateScoped
创建一个具有指定范围的新实例。
像这样用范围内的结果重新分配您的凭据变量,它应该可以工作:
if (_cred.IsCreateScopedRequired) {
_cred = _cred.CreateScoped(AnalyticsService.Scope.Analytics);
}
可接受的答案之所以有用,是因为它以一种更加困难的方式实现了同一件事。
答案 5 :(得分:0)
如果您在尝试确定如何创建 ServiceAccountCredential
时到达这里,而不直接使用密钥文件,您可能有兴趣了解以下方法(有时)是否有效:
GoogleCredential credential = GoogleCredential.GetApplicationDefault();
ServiceAccountCredential serviceAccountCredential =
credential.UnderlyingCredential as ServiceAccountCredential;