ASP.NET MVC中Google OAuth2授权后result.Credential为空问题
ASP.NET MVC Google Sheets长期授权问题解决
问题描述
我在ASP.NET MVC项目中用以下代码实现授权并刷新令牌,以此更新Google表格数据。已知令牌1小时后过期,所以我做了过期检查并尝试刷新,但问题是1小时后result.Credential为空,且result.redirectUri指向需要重新连接账户的地址。我想实现长期授权,备注:FileDataStore工作正常,令牌数据已保存到C:\Token。
原代码
private async Task SendToGooggleSheet(List<string> columns, List<List<string>> rows, int formId, int answerId, string userId, string sheetid) { try { TempData["FormUserId"] = userId + "_" + formId; CancellationToken cancellationToken; CancellationTokenSource cts = new CancellationTokenSource(); cts.CancelAfter(TimeSpan.FromSeconds(50)); cancellationToken = cts.Token; var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()). AuthorizeAsync(cancellationToken); if (result.Credential != null) { if (result.Credential.Token.IsExpired(SystemClock.Default)) { //var accessToekn = await result.Credential.GetAccessTokenForRequestAsync(); // this 2 line works for refresh token var refreshResult = await result.Credential.RefreshTokenAsync(cancellationToken); } // .... 后续Google表格操作代码 } // ... 其他逻辑 } catch(Exception ex) { // 异常处理 } } ///////////////////// public class AppFlowMetadata : FlowMetadata { private static readonly IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer { ClientSecrets = new ClientSecrets { ClientId = ePoll.Model.GlobalApplicationData.GoogleDriveAuthorizationClientId, ClientSecret = ePoll.Model.GlobalApplicationData.GoogleDriveAuthorizationSecretId }, Scopes = new[] { DriveService.Scope.DriveFile,SheetsService.Scope.DriveFile}, DataStore = new FileDataStore(ePoll.Model.GlobalApplicationData.GoogleSheetOauthSaveFolder, true), //C:\Token }); public override string GetUserId(Controller controller) { var user = controller.TempData["FormUserId"]; return user?.ToString(); } public override IAuthorizationCodeFlow Flow { get { return flow; } } }
解决方案
问题根源
TempData生命周期限制:GetUserId依赖TempData存储用户标识,但TempData仅在单次请求中有效,后续请求时标识丢失,导致Flow无法找到对应存储的令牌,触发重新授权流程。- 手动刷新冗余:Google .NET客户端库会自动检测令牌过期并完成刷新,无需手动调用
RefreshTokenAsync。 - 缺少离线访问配置:未明确请求
offline访问类型,可能导致无法获取长期有效的刷新令牌。
修改步骤
1. 替换TempData为持久化用户标识
将GetUserId中的TempData替换为Session或稳定的用户身份标识(如登录用户的ID),确保跨请求能获取到一致的用户ID:
public override string GetUserId(Controller controller) { // 优先从Session获取,不存在则初始化 var userId = controller.Session["FormUserId"]?.ToString(); if (string.IsNullOrEmpty(userId)) { // 可根据实际场景从请求参数、登录信息等获取 userId = controller.Request.QueryString["userId"] + "_" + controller.Request.QueryString["formId"]; controller.Session["FormUserId"] = userId; } return userId; }
同时修改SendToGooggleSheet中的标识存储:
// 替换原TempData赋值 Session["FormUserId"] = userId + "_" + formId;
2. 配置离线访问权限
在GoogleAuthorizationCodeFlow初始化时添加AccessType = "offline",确保获取刷新令牌以实现长期授权:
private static readonly IAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer { ClientSecrets = new ClientSecrets { ClientId = ePoll.Model.GlobalApplicationData.GoogleDriveAuthorizationClientId, ClientSecret = ePoll.Model.GlobalApplicationData.GoogleDriveAuthorizationSecretId }, Scopes = new[] { DriveService.Scope.DriveFile, SheetsService.Scope.DriveFile }, DataStore = new FileDataStore(ePoll.Model.GlobalApplicationData.GoogleSheetOauthSaveFolder, true), AccessType = "offline", // 请求离线访问,获取刷新令牌 ApprovalPrompt = "auto" // 自动复用已有授权,避免重复确认 });
3. 简化令牌使用逻辑
移除手动过期检查和刷新调用,直接使用Credential,库会自动处理令牌刷新:
var result = await new AuthorizationCodeMvcApp(this, new AppFlowMetadata()).AuthorizeAsync(cancellationToken); if (result.Credential != null) { // 初始化Sheets服务,直接使用Credential即可 var sheetsService = new SheetsService(new BaseClientService.Initializer { HttpClientInitializer = result.Credential, ApplicationName = "你的应用名称" }); // 执行Google表格更新操作 } else { // 未授权时跳转至授权页面 return Redirect(result.RedirectUri); }
验证要点
- 首次授权后,检查
C:\Token目录下是否生成了包含刷新令牌的存储文件。 - 等待1小时后再次发起请求,确认
result.Credential不为空,且无需用户重新授权。
内容的提问来源于stack exchange,提问作者Amir
相关产品推荐
相关产品推荐

