7

在我的应用程序中,我使用Google API V 3.0 .Net库与Google OAuth2同步Google日历和Outlook日历。我使用下面的代码来获取Google.Apis.Calendar.v3.CalendarService服务对象。 在身份验证期间,我存储了Json文件,并从中请求Google.Apis.Auth.OAuth2.UserCredential对象。Google API V 3.0 .Net库和Google OAuth2如何处理刷新令牌

private Google.Apis.Auth.OAuth2.UserCredential GetGoogleOAuthCredential() 
{ 
    GoogleTokenModel _TokenData = new GoogleTokenModel(); 
    String JsonFilelocation = "jsonFileLocation; 
    Google.Apis.Auth.OAuth2.UserCredential credential = null; 
    using (var stream = new FileStream(JsonFilelocation, FileMode.Open, 
        FileAccess.Read)) 
    { 
     Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.Folder = "Tasks.Auth.Store"; 
     credential = Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.AuthorizeAsync(
     Google.Apis.Auth.OAuth2.GoogleClientSecrets.Load(stream).Secrets, 
     new[] { Google.Apis.Calendar.v3.CalendarService.Scope.Calendar }, 
     "user", 
     CancellationToken.None, 
     new FileDataStore("OGSync.Auth.Store")).Result; 
    } 
    return credential; 
} 

请求为服务对象的代码是:

Google.Apis.Calendar.v3.CalendarService _V3calendarService = new Google.Apis.Calendar.v3.CalendarService(new Google.Apis.Services.BaseClientService.Initializer() 
{ 
HttpClientInitializer = GetGoogleOAuthCredential(), 
ApplicationName = "TestApplication", 
}); 

上面的代码工作正常,以获得Calendarservice对象。我的问题是,我的Json文件有刷新和访问令牌。当访问令牌过期时,上述代码如何处理刷新令牌以获取服务?因为我需要经常调用Calendarservice对象,所以我喜欢为calenderService对象实现单例模式。如何在不经常调用GetGoogleOAuthCredential的情况下获取Calendarservice?任何帮助/指导表示赞赏。

+0

还要检查http://stackoverflow.com/a/24972426/833846 – 2017-02-24 12:43:15

回答

2

这就是客户端库的问题!这个魔法自动完成:)

UserCredential实现IHttpExecuteInterceptor和IHttpUnsuccessfulResponseHandler。所以无论访问令牌何时过期或已过期,客户端都会向授权服务器发出调用以刷新令牌并获取新的访问令牌(该访问令牌在接下来的60分钟内有效)。

阅读在https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth#credentials

+0

好,谢谢,那么每次我是否需要致电授权服务器?有什么办法来检查令牌是否过期,并在必要时进行服务器调用? – 2014-09-30 03:14:39

+0

图书馆为你做。 – peleyal 2014-09-30 04:05:28

+0

使用IMAP客户端时,UserCredential不会刷新令牌。也许它会在使用Google API服务对象时发生,但不是在所有情况下。 – skyfree 2016-04-11 01:23:54

1

更多关于它对于我来说,客户端库没有做清爽,甚至没有创造刷新令牌。
我检查令牌是否过期并刷新。 (令牌有1小时的续航时间)。 credential.Token.IsExpired会告诉你它是否过期,credential.Token.RefreshToken(用户标识符)将刷新必要的标记。

12

花了我最后两天搞清楚了这一点。除非您指定“access_type = offline”,否则库不会自动刷新令牌。

https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth

我会贴上我使用的代码,如果您有什么不明白,就问我。我已经阅读了很多文章,而且我现在正在努力工作,所以有一些评论代码,它还没有被重构。我希望这会帮助某人。我使用的NuGet包是这些:

Google.Apis.Auth.MVC

Google.Apis.Calendar。V3

代码:

AuthCallbackController:

[AuthorizationCodeActionFilter] 
public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController 
{ 
protected static readonly ILogger Logger = ApplicationContext.Logger.ForType<AuthCallbackController>(); 

/// <summary>Gets the authorization code flow.</summary> 
protected IAuthorizationCodeFlow Flow { get { return FlowData.Flow; } } 

/// <summary> 
/// Gets the user identifier. Potential logic is to use session variables to retrieve that information. 
/// </summary> 
protected string UserId { get { return FlowData.GetUserId(this); } } 

/// <summary> 
/// The authorization callback which receives an authorization code which contains an error or a code. 
/// If a code is available the method exchange the coed with an access token and redirect back to the original 
/// page which initialized the auth process (using the state parameter). 
/// <para> 
/// The current timeout is set to 10 seconds. You can change the default behavior by setting 
/// <see cref="System.Web.Mvc.AsyncTimeoutAttribute"/> with a different value on your controller. 
/// </para> 
/// </summary> 
/// <param name="authorizationCode">Authorization code response which contains the code or an error.</param> 
/// <param name="taskCancellationToken">Cancellation token to cancel operation.</param> 
/// <returns> 
/// Redirect action to the state parameter or <see cref="OnTokenError"/> in case of an error. 
/// </returns> 
[AsyncTimeout(60000)] 
public async override Task<ActionResult> IndexAsync(AuthorizationCodeResponseUrl authorizationCode, 
    CancellationToken taskCancellationToken) 
{ 
    if (string.IsNullOrEmpty(authorizationCode.Code)) 
    { 
     var errorResponse = new TokenErrorResponse(authorizationCode); 
     Logger.Info("Received an error. The response is: {0}", errorResponse); 
     Debug.WriteLine("Received an error. The response is: {0}", errorResponse); 
     return OnTokenError(errorResponse); 
    } 

    Logger.Debug("Received \"{0}\" code", authorizationCode.Code); 
    Debug.WriteLine("Received \"{0}\" code", authorizationCode.Code); 


    var returnUrl = Request.Url.ToString(); 
    returnUrl = returnUrl.Substring(0, returnUrl.IndexOf("?")); 

    var token = await Flow.ExchangeCodeForTokenAsync(UserId, authorizationCode.Code, returnUrl, 
     taskCancellationToken).ConfigureAwait(false); 

    // Extract the right state. 
    var oauthState = await AuthWebUtility.ExtracRedirectFromState(Flow.DataStore, UserId, 
     authorizationCode.State).ConfigureAwait(false); 

    return new RedirectResult(oauthState); 
} 

protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData 
{ 
    get { return new AppFlowMetadata(); } 
} 

protected override ActionResult OnTokenError(TokenErrorResponse errorResponse) 
{ 
    throw new TokenResponseException(errorResponse); 
} 


//public class AuthCallbackController : Google.Apis.Auth.OAuth2.Mvc.Controllers.AuthCallbackController 
//{ 
// protected override Google.Apis.Auth.OAuth2.Mvc.FlowMetadata FlowData 
// { 
//  get { return new AppFlowMetadata(); } 
// } 
//} 

}

用于控制器调用谷歌API

public async Task<ActionResult> GoogleCalendarAsync(CancellationToken cancellationToken) 
    { 
     var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()). 
      AuthorizeAsync(cancellationToken); 

     if (result.Credential != null) 
     { 
      //var ttt = await result.Credential.RevokeTokenAsync(cancellationToken); 

      //bool x = await result.Credential.RefreshTokenAsync(cancellationToken); 

      var service = new CalendarService(new BaseClientService.Initializer() 
      { 
       HttpClientInitializer = result.Credential, 
       ApplicationName = "GoogleApplication", 
      }); 
      var t = service.Calendars; 

      var tt = service.CalendarList.List(); 

      // Define parameters of request. 
      EventsResource.ListRequest request = service.Events.List("primary"); 
      request.TimeMin = DateTime.Now; 
      request.ShowDeleted = false; 
      request.SingleEvents = true; 
      request.MaxResults = 10; 
      request.OrderBy = EventsResource.ListRequest.OrderByEnum.StartTime; 

      // List events. 
      Events events = request.Execute(); 
      Debug.WriteLine("Upcoming events:"); 
      if (events.Items != null && events.Items.Count > 0) 
      { 
       foreach (var eventItem in events.Items) 
       { 
        string when = eventItem.Start.DateTime.ToString(); 
        if (String.IsNullOrEmpty(when)) 
        { 
         when = eventItem.Start.Date; 
        } 
        Debug.WriteLine("{0} ({1})", eventItem.Summary, when); 
       } 
      } 
      else 
      { 
       Debug.WriteLine("No upcoming events found."); 
      } 


      //Event myEvent = new Event 
      //{ 
      // Summary = "Appointment", 
      // Location = "Somewhere", 
      // Start = new EventDateTime() 
      //  { 
      //   DateTime = new DateTime(2014, 6, 2, 10, 0, 0), 
      //   TimeZone = "America/Los_Angeles" 
      //  }, 
      // End = new EventDateTime() 
      //  { 
      //   DateTime = new DateTime(2014, 6, 2, 10, 30, 0), 
      //   TimeZone = "America/Los_Angeles" 
      //  }, 
      // Recurrence = new String[] { 
      //  "RRULE:FREQ=WEEKLY;BYDAY=MO" 
      //  }, 
      // Attendees = new List<EventAttendee>() 
      //  { 
      //  new EventAttendee() { Email = "[email protected]" } 
      //  } 
      //}; 

      //Event recurringEvent = service.Events.Insert(myEvent, "primary").Execute(); 

      return View(); 
     } 
     else 
     { 
      return new RedirectResult(result.RedirectUri); 
     } 
    } 

派生类FlowMetadata

0123的方法

实体框架6数据存储类

public class EFDataStore : IDataStore 
    { 
     public async Task ClearAsync() 
     { 
      using (var context = new ApplicationDbContext()) 
      { 
       var objectContext = ((IObjectContextAdapter)context).ObjectContext; 
       await objectContext.ExecuteStoreCommandAsync("TRUNCATE TABLE [Items]"); 
      } 
     } 

     public async Task DeleteAsync<T>(string key) 
     { 
      if (string.IsNullOrEmpty(key)) 
      { 
       throw new ArgumentException("Key MUST have a value"); 
      } 

     using (var context = new ApplicationDbContext()) 
     { 
      var generatedKey = GenerateStoredKey(key, typeof(T)); 
      var item = context.GoogleAuthItems.FirstOrDefault(x => x.Key == generatedKey); 
      if (item != null) 
      { 
       context.GoogleAuthItems.Remove(item); 
       await context.SaveChangesAsync(); 
      } 
     } 
    } 

    public Task<T> GetAsync<T>(string key) 
    { 
     if (string.IsNullOrEmpty(key)) 
     { 
      throw new ArgumentException("Key MUST have a value"); 
     } 

     using (var context = new ApplicationDbContext()) 
     { 
      var generatedKey = GenerateStoredKey(key, typeof(T)); 
      var item = context.GoogleAuthItems.FirstOrDefault(x => x.Key == generatedKey); 
      T value = item == null ? default(T) : JsonConvert.DeserializeObject<T>(item.Value); 
      return Task.FromResult<T>(value); 
     } 
    } 

    public async Task StoreAsync<T>(string key, T value) 
    { 
     if (string.IsNullOrEmpty(key)) 
     { 
      throw new ArgumentException("Key MUST have a value"); 
     } 

     using (var context = new ApplicationDbContext()) 
     { 
      var generatedKey = GenerateStoredKey(key, typeof(T)); 
      string json = JsonConvert.SerializeObject(value); 

      var item = await context.GoogleAuthItems.SingleOrDefaultAsync(x => x.Key == generatedKey); 

      if (item == null) 
      { 
       context.GoogleAuthItems.Add(new GoogleAuthItem { Key = generatedKey, Value = json }); 
      } 
      else 
      { 
       item.Value = json; 
      } 

      await context.SaveChangesAsync(); 
     } 
    } 

    private static string GenerateStoredKey(string key, Type t) 
    { 
     return string.Format("{0}-{1}", t.FullName, key); 
    } 
} 

为GoogleAuthorizationCodeFlow派生的类。启用自动“刷新”令牌的长效刷新令牌,这意味着获得新的访问令牌。

https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth

internal class ForceOfflineGoogleAuthorizationCodeFlow : GoogleAuthorizationCodeFlow 
{ 
    public ForceOfflineGoogleAuthorizationCodeFlow(GoogleAuthorizationCodeFlow.Initializer initializer) : base (initializer) { } 

    public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(string redirectUri) 
    { 
     return new GoogleAuthorizationCodeRequestUrl(new Uri(AuthorizationServerUrl)) 
     { 
      ClientId = ClientSecrets.ClientId, 
      Scope = string.Join(" ", Scopes), 
      RedirectUri = redirectUri, 
      AccessType = "offline", 
      ApprovalPrompt = "force" 
     }; 
    } 
} 

GoogleAuthItem用于与EFDataStore

当我需要的服务
public class GoogleAuthItem 
{ 
    [Key] 
    [MaxLength(100)] 
    public string Key { get; set; } 

    [MaxLength(500)] 
    public string Value { get; set; } 
} 

public DbSet<GoogleAuthItem> GoogleAuthItems { get; set; } 
+0

感谢分享,似乎很大的解决方法(Y) – 2015-08-06 06:43:19

+0

另请参阅http://stackoverflow.com/a/24972426/833846 – 2017-02-24 12:43:05