当我使用Google.Apis.Auth.OAuth2.UserCredential
授权时,我的代码效果很好。当我切换到Google.Apis.Auth.OAuth2.ServiceAccountCredential
时,相同的代码不起作用。
麻烦可能出现在以下三个地方之一:
1)。我正确构建ServiceAccountCredential
吗?
2)。我是否正确使用ServiceAccountCredential
来访问
用户的帐户?
3)。 GA管理员是否为服务帐户提供了正确的读取权限 用户的邮件?
这是无效的代码:
using Google.Apis.Auth.OAuth2;
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using Google.Apis.Services;
using Google.Apis.Discovery.v1;
using Google.Apis.Discovery.v1.Data;
using Google.Apis.Util.Store;
string[] asScopes = { GmailService.Scope.GmailModify };
string msApplicationName = "Gmail API .NET Quickstart";
string sClientEmail = "blah@blah.gserviceaccount.com" //service account;
string sUser = "cfo@mydomain.com" //email of the user that I want to read;
string sPrivateKey = "-----BEGIN PRIVATE KEY----- blah"//service account private key;
string[] asScopes = {"https://mail.google.com/"};
//get credential
ServiceAccountCredential oCred = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(sClientEmail)
{
Scopes = asScopes,
User = sUser //the user to be impersonated
}.FromPrivateKey(sPrivateKey));
// Create Gmail API service.
GmailService oSVC = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = oCred,
ApplicationName = msApplicationName,
});
// List labels.
UsersResource.LabelsResource.ListRequest request = oSVC.Users.Labels.List("me");
IList<Google.Apis.Gmail.v1.Data.Label> labels = request.Execute().Labels; //<--error here.
/* --- fails with:
Google.Apis.Auth.OAuth2.Responses.TokenResponseException was unhandled HResult=-2146233088 Message=Error:"unauthorized_client", Description:"Client is unauthorized to retrieve access tokens using this method.", Uri:"" Source=Google.Apis.Auth
*/
如果有人可以提供有关如何测试ServiceAccountCredential
以确定其构造是否正确的示例,以及进一步授权的内容,我真的很感激。
所有这一个令人烦恼的问题是,如果我甚至可以从PrivateKey创建ServiceAccountCredential
,因为我看到的所有示例都使用证书,例如:
var certificate = new X509Certificate2("key2.p12", "notasecret",
X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
string userEmail = "abc@gmail.com";
ServiceAccountCredential credential = new ServiceAccountCredential(
new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new string[] { Gmail.v1.GmailService.Scope.GmailReadonly }
}.FromCertificate(certificate)
);
答案 0 :(得分:1)
YAHOO!我有工作了!
所以关键是: 1)。创建服务帐户时必须检查DWD。 strong> 2)。为了使应用程序获得我需要使用的范围的确切授权。 因此,您的Google管理员必须为您的应用提供所需的确切范围。
代码如下:
private const string MC_GOOGLE_APP_NAME = "from-google-00110";
private const string MC_GOOGLE_SERVICE_ACCOUNT = "appname@from-google-00110.iam.gserviceaccount.com";
private const string MC_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\xxxx blah blah blah xxx\n-----END PRIVATE KEY-----\n";
private const string MS_GMAIL_QUERY = "label: inbox, label: unread";
//**** CRITICAL! THIS MUST EXACTLY MATCH THE SCOPES THAT YOUR App IS AUTHORZIED FOR ***
private const string MS_SCOPES ="https://www.googleapis.com/auth/gmail.modify";
private string msEmailAccount = "mike@blue-mosaic.com"; //the email account you are reading, not mine please
//get credential for service account
ServiceAccountCredential credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(MC_GOOGLE_SERVICE_ACCOUNT)
{
Scopes = MS_SCOPES,
User = msEmailAccount
}.FromPrivateKey(MC_PRIVATE_KEY));
//create a new gmail service
GmailService oSVC = GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = MC_GOOGLE_APP_NAME,
});
List<Google.Apis.Gmail.v1.Data.Message> aoMessageList = new List<Google.Apis.Gmail.v1.Data.Message>();
UsersResource.MessagesResource.ListRequest request = oSVC.Users.Messages.List(msEmailAccount);
request.Q = MS_GMAIL_QUERY;
//keep making requests until all messages are read.
do
{
ListMessagesResponse response = request.Execute();
if (response.Messages != null)
{
aoMessageList.AddRange(response.Messages);
request.PageToken = response.NextPageToken;
}
} while (!String.IsNullOrEmpty(request.PageToken));
//now read the body of each of the new messages
foreach (Message oMsg in aoMessageList)
{
string sMsgID = oMsg.Id;
sState = "Reading Message '" + sMsgID + "'";
// and this one gets a bit nuts. processing GMAIL messages took me a fair amount
// of reading, parsing and decoding, so it required a whole class!!
SaneMessage oThisMsg = new SaneMessage(oSVC, "me", sMsgID);
//and do something with the message
}
因此,上面的代码不仅显示已登录,还显示正在阅读电子邮件。要阅读Google Apps电子邮件,需要进行大量的解析处理,以处理URL编码的Base64编码消息,这些消息中经常出现奇怪的中断。我写了一个课程来处理所有这一切:
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Email_Reader_Service
{
class SaneMessage
{
private string msID = "";
private string msFrom = "";
private string msDate = "";
private string msSubject = "";
private string msBody = "";
public string ID
{
get { return msID; }
}
public string From
{
get { return msFrom; }
}
public DateTime Date
{
get { return Convert.ToDateTime(msDate); }
}
public string Subject
{
get { return msSubject; }
}
public string Body
{
get { return msBody; }
}
public SaneMessage(GmailService service, String userId, String messageId)
{
Google.Apis.Gmail.v1.Data.Message oStupidMessage = service.Users.Messages.Get(userId, messageId).Execute();
string sBackupDate = string.Empty;
foreach (var mParts in oStupidMessage.Payload.Headers)
{
System.Diagnostics.Debug.Print("{0}\t\t\t\t{1}", mParts.Name, mParts.Value);
switch (mParts.Name)
{
case ("X-Google-Original-Date"):
msDate = mParts.Value;
break;
case ("Date"):
sBackupDate = mParts.Value;
break;
case ("From"):
msFrom = mParts.Value;
break;
case ("Subject"):
msSubject = mParts.Value;
break;
}
}
//-----------------------------------------------------------------------------------------------
//the fooking date comes in a plethora of formats. if the timezone name is appended on the end
// the datetime conversion can't convert.
if(msDate.Length == 0)
msDate = sBackupDate;
if (msDate.Contains('('))
msDate= msDate.Substring(0, msDate.LastIndexOf('('));
//-----------------------------------------------------------------------------------------------
if (msDate != "" && msFrom != "")
{
string sEncodedBody;
if (oStupidMessage.Payload.Parts == null && oStupidMessage.Payload.Body != null)
{
sEncodedBody = oStupidMessage.Payload.Body.Data;
}
else
{
sEncodedBody = getNestedParts(oStupidMessage.Payload.Parts, "");
}
///need to replace some characters as the data for the email's body is base64
msBody = DecodeURLEncodedBase64EncodedString(sEncodedBody);
}
}
private string getNestedParts(IList<MessagePart> part, string curr)
{
string str = curr;
if (part == null)
{
return str;
}
else
{
foreach (var parts in part)
{
if (parts.Parts == null)
{
if (parts.Body != null && parts.Body.Data != null)
{
str += parts.Body.Data;
}
}
else
{
return getNestedParts(parts.Parts, str);
}
}
return str;
}
}
/// <summary>
/// Turn a URL encoded base64 encoded string into readable UTF-8 string.
/// </summary>
/// <param name="sInput">base64 URL ENCODED string.</param>
/// <returns>UTF-8 formatted string</returns>
private string DecodeURLEncodedBase64EncodedString(string sInput)
{
string[] asInput = sInput.Split("=".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
string sOutput = string.Empty;
foreach (string sInputPiece in asInput)
{
string sBase46codedBody = sInputPiece.Replace("-", "+").Replace("_", "/"); //get rid of URL encoding, and pull any current padding off.
string sPaddedBase46codedBody = sBase46codedBody.PadRight(sBase46codedBody.Length + (4 - sBase46codedBody.Length % 4) % 4, '='); //re-pad the string so it is correct length.
byte[] data = Convert.FromBase64String(sPaddedBase46codedBody);
sOutput += Encoding.UTF8.GetString(data);
}
System.Diagnostics.Debug.Print("{0}\r\n\r\n", sOutput);
return sOutput;
}
}
}