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