gpt4 book ai didi

c# - 将 OAuth token 从 Google API 响应保存到数据库?

转载 作者:行者123 更新时间:2023-12-02 05:07:02 24 4
gpt4 key购买 nike

我正在构建一个 MVC 应用程序,它将对我们网站上希望我们将事件发布到他们的日历中的用户进行身份验证。我已经安装了 .NET C# 客户端库以及 Auth 和 MVC 包。

使用 Google.Apis.Auth.OAuth2.Mvc 我在 IDataStore 实现中访问 TokenResponse 时遇到问题,而是将 URL 保存为 token ,即本地主机:6055/calendar/calendarasync/deantest04556734

我发现的所有示例似乎都已过时,没有使用 Mvc 包,也没有实现 DataStore 来保存到数据库,因此我使用了 Daimto 示例和官方示例中的一些代码开始吧。

任何人都可以指出我正确的方向或看到我的代码有任何问题吗?

数据库数据存储

public class DatabaseDataStore : IDataStore
{
private SqlConnection connection;

public DatabaseDataStore(SqlConnection sqlConn)
{
if (sqlConn != null)
{
connection = sqlConn;

if(connection.State != ConnectionState.Open)
connection.Open();
}
}

/// <summary>
/// Stores the given value for the given key. It creates a new file (named <see cref="GenerateStoredKey"/>) in
/// <see cref="FolderPath"/>.
/// </summary>
/// <typeparam name="T">The type to store in the data store</typeparam>
/// <param name="key">The key</param>
/// <param name="value">The value to store in the data store</param>
public Task StoreAsync<T>(string key, T value)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}

var serialized = NewtonsoftJsonSerializer.Instance.Serialize(value);
string userId = getUserId(key);

if (userId == null)
{
insertUserData(key, serialized);
}
else
{
updateUserData(userId, key, serialized);
}

return Task.Delay(0);
}

/// <summary>
/// Returns the stored value for the given key or <c>null</c> if the matching file (<see cref="GenerateStoredKey"/>
/// in <see cref="FolderPath"/> doesn't exist.
/// </summary>
/// <typeparam name="T">The type to retrieve</typeparam>
/// <param name="key">The key to retrieve from the data store</param>
/// <returns>The stored object</returns>
public Task<T> GetAsync<T>(string key)
{
//Key is the user string sent with AuthorizeAsync
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}

TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();
string refreshToken = null;

// Try and find the Row in the DB.
using (SqlCommand cmd = new SqlCommand("Calendar_GetRefreshToken", connection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 2700;
try
{
cmd.Parameters.AddWithValue("@username", key);
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
refreshToken = reader["RefreshToken"].ToString();
}
reader.Dispose();

if (refreshToken == null)
{
// we don't have a record so we request it of the user.
tcs.SetResult(default(T));
}
else
{
try
{
// we have it we use that.
tcs.SetResult(NewtonsoftJsonSerializer.Instance.Deserialize<T>(refreshToken));
}
catch (Exception ex)
{
tcs.SetException(ex);
}
}
}
catch (Exception ex)
{
//logger.Error("Method:CheckLocalProperty - id: " + propId + " - Error:" + ex.Message);
return null;
}
}

return tcs.Task;
}

/// <summary>
/// Clears all values in the data store. This method deletes all files in <see cref="FolderPath"/>.
/// </summary>
public Task ClearAsync()
{
// Removes all data from the Table.
string truncateString = "truncate table [dbo].[tblCactusGoogleUsers] ";
SqlCommand commandins = new SqlCommand(truncateString, connection);
commandins.ExecuteNonQuery();

return Task.Delay(0);
}

/// <summary>
/// Deletes the given key. It deletes the <see cref="GenerateStoredKey"/> named file in <see cref="FolderPath"/>.
/// </summary>
/// <param name="key">The key to delete from the data store</param>
public Task DeleteAsync<T>(string key)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException("Key MUST have a value");
}

deleteUserData(key);

return Task.Delay(0);
}

/// <summary>Creates a unique stored key based on the key and the class type.</summary>
/// <param name="key">The object key</param>
/// <param name="t">The type to store or retrieve</param>
public static string GenerateStoredKey(string key, Type t)
{
return string.Format("{0}-{1}", t.FullName, key);
}

private string getUserId(string value)
{
using (SqlCommand cmd = new SqlCommand("Calendar_GetUserId", connection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 2700;
try
{
cmd.Parameters.AddWithValue("@username", value);
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
return reader["UserId"].ToString();
}
reader.Dispose();
}
catch (Exception ex)
{
//logger.Error("Method:CheckLocalProperty - id: " + propId + " - Error:" + ex.Message);
return null;
}
}
return null;
}

private void insertUserData(string key, string value)
{
using (SqlCommand cmd = new SqlCommand("Calendar_InsertUser", connection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 2700;
try
{
cmd.Parameters.AddWithValue("@token", value);
cmd.Parameters.AddWithValue("@username", key);
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
//logger.Error("Method:insertUserData - id: " + key + " - Error:" + ex.Message);
}
}
}

private void updateUserData(string userId, string key, string value)
{
using (SqlCommand cmd = new SqlCommand("Calendar_UpdateUser", connection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 2700;
try
{
cmd.Parameters.AddWithValue("@userid", userId);
cmd.Parameters.AddWithValue("@username", key);
cmd.Parameters.AddWithValue("@token", value);
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
//logger.Error("Method:updateUserData - id: " + key + " - Error:" + ex.Message);
}
}
}

private void deleteUserData(string key)
{
using (SqlCommand cmd = new SqlCommand("Calendar_DeleteUser", connection))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 2700;
try
{
cmd.Parameters.AddWithValue("@username", key);
cmd.ExecuteNonQuery();
}
catch (Exception ex)
{
//logger.Error("Method:deleteUserData - id: " + key + " - Error:" + ex.Message);
}
}
}
}

流元数据

public class AppAuthFlowMetadata : FlowMetadata
{
private SqlConnection connection;
private readonly IAuthorizationCodeFlow flow;

public AppAuthFlowMetadata(SqlConnection sqlConn, string clientId, string clientSecret)
{
if (sqlConn != null)
{
connection = sqlConn;

if (connection.State != ConnectionState.Open)
connection.Open();

flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = clientId,
ClientSecret = clientSecret
},
Scopes = new[] {
CalendarService.Scope.Calendar
},
DataStore = new DatabaseDataStore(connection)
});
}
else
{
throw new ArgumentException("sqlConn is null");
}
}

public override string GetUserId(Controller controller)
{
/* TODO - Get UserId from form post */
return controller.User.Identity.Name;
}

public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}
}

Controller

public class CalendarController : Controller
{
CalendarService service;
string CLIENT_ID = ConfigurationManager.AppSettings["GoogleClientID"].ToString();
string CLIENT_SECRET = ConfigurationManager.AppSettings["GoogleClientSecret"].ToString();

[Authorize]
public async Task<ActionResult> CalendarAsync(CancellationToken cancellationToken)
{
ViewBag.Message = "Your calendar page.";

var result = await new AuthorizationCodeMvcApp(this, new AppAuthFlowMetadata(
new SqlConnection(ConfigurationManager.ConnectionStrings["HFConnString"].ConnectionString),
CLIENT_ID,
CLIENT_SECRET)
).AuthorizeAsync(cancellationToken);

if (result.Credential == null)
return new RedirectResult(result.RedirectUri);

service = new CalendarService(new BaseClientService.Initializer
{
HttpClientInitializer = result.Credential,
ApplicationName = "ASP.NET Google APIs MVC Sample"
});
...
}
}

最佳答案

过去两天我自己解决了这个问题。我将粘贴我正在使用的代码,如果有任何不明白的地方,请询问。我读过很多帖子,现在我几乎已经开始工作了,所以有一些注释过的代码,但它还没有被重构。我希望这会对某人有所帮助。我正在使用的 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(); }
// }
//}

}

Controller调用Google 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 = "johndoe@gmail.com" }
// }
//};

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

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

FlowMetadata的派生类

public class AppFlowMetadata : FlowMetadata
{
//static readonly string server = ConfigurationManager.AppSettings["DatabaseServer"];
//static readonly string serverUser = ConfigurationManager.AppSettings["DatabaseUser"];
//static readonly string serverPassword = ConfigurationManager.AppSettings["DatabaseUserPassword"];
//static readonly string serverDatabase = ConfigurationManager.AppSettings["DatabaseName"];
////new FileDataStore("Daimto.GoogleCalendar.Auth.Store")
////new FileDataStore("Drive.Api.Auth.Store")
//static DatabaseDataStore databaseDataStore = new DatabaseDataStore(server, serverUser, serverPassword, serverDatabase);


private static readonly IAuthorizationCodeFlow flow =
new ForceOfflineGoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = new ClientSecrets
{
ClientId = "yourClientId",
ClientSecret = "yourClientSecret"

},
Scopes = new[]
{
CalendarService.Scope.Calendar, // Manage your calendars
//CalendarService.Scope.CalendarReadonly // View your Calendars
},
DataStore = new EFDataStore(),
});

public override string GetUserId(Controller controller)
{
// In this sample we use the session to store the user identifiers.
// That's not the best practice, because you should have a logic to identify
// a user. You might want to use "OpenID Connect".
// You can read more about the protocol in the following link:
// https://developers.google.com/accounts/docs/OAuth2Login.

//var user = controller.Session["user"];
//if (user == null)
//{
// user = Guid.NewGuid();
// controller.Session["user"] = user;
//}
//return user.ToString();

//var store = new UserStore<ApplicationUser>(new ApplicationDbContext());
//var manager = new UserManager<ApplicationUser>(store);
//var currentUser = manager.FindById(controller.User.Identity.GetUserId());

return controller.User.Identity.GetUserId();

}

public override IAuthorizationCodeFlow Flow
{
get { return flow; }
}

public override string AuthCallback
{
get { return @"/GoogleApplication/AuthCallback/IndexAsync"; }
}
}

Entity Framework 6 DataStore类

 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 的派生类。启用长期刷新 token 来自动“刷新” token ,这意味着获取新的访问 token 。

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

关于c# - 将 OAuth token 从 Google API 响应保存到数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29776725/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com