You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

解决方案

问题根源

  1. TempData生命周期限制:GetUserId依赖TempData存储用户标识,但TempData仅在单次请求中有效,后续请求时标识丢失,导致Flow无法找到对应存储的令牌,触发重新授权流程。
  2. 手动刷新冗余:Google .NET客户端库会自动检测令牌过期并完成刷新,无需手动调用RefreshTokenAsync。
  3. 缺少离线访问配置:未明确请求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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 21:29:53