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

ASP.NET Core MVC中如何获取Google Photos Library API刷新令牌

Google Photos Library API 刷新令牌获取与后端授权方案

核心问题解决:获取并使用刷新令牌

Google Photos Library API 支持刷新令牌,你当前的问题是未正确保存和使用刷新令牌。只要授权时指定access_type=offline(你已在URL中加入该参数),首次授权后Google会返回refresh_token,后续用它即可刷新access_token,无需用户再次手动授权。

修正步骤:

  1. 持久化保存Refresh Token
    你的Callback方法中,ExchangeCodeForTokenAsync返回的tokenResponse不仅包含AccessToken,还会携带RefreshToken(仅首次授权返回,后续用refresh token换access token时不会再返回新的refresh token)。需将refresh token按用户维度存储到数据库,而非仅存access token:

    public async Task<IActionResult> Callback(string code, string error)
    {
        if (!string.IsNullOrEmpty(error))
        {
            return Content($"Error: {error}");
        }
    
        if (string.IsNullOrEmpty(code))
        {
            return Content("Authorization code not received.");
        }
    
        var tokenResponse = await ExchangeCodeForTokenAsync(code);
    
        // 同时保存Access Token、Refresh Token及过期时间到数据库
        _userTokenService.SaveUserTokens(userId, tokenResponse.AccessToken, tokenResponse.RefreshToken, DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn));
    
        HttpContext.Session.SetString("AccessToken", tokenResponse.AccessToken);
    
        return RedirectToAction("Index", "Home");
    }
    
  2. 用Refresh Token刷新Access Token
    调用API前先检查access token是否即将过期,若过期则用refresh token获取新的access token:

    // 获取有效Access Token的工具方法
    private async Task<string> GetValidAccessToken(string userId)
    {
        var userTokens = _userTokenService.GetUserTokens(userId);
        // 提前5分钟刷新,避免token在请求过程中过期
        if (DateTime.UtcNow < userTokens.ExpiresAt.AddMinutes(-5))
        {
            return userTokens.AccessToken;
        }
    
        // 调用Google令牌端点刷新access token
        var client = new HttpClient();
        var content = new FormUrlEncodedContent(new Dictionary<string, string>
        {
            {"client_id", "[CLIENT_ID]"},
            {"client_secret", "[CLIENT_SECRET]"},
            {"refresh_token", userTokens.RefreshToken},
            {"grant_type", "refresh_token"}
        });
        var response = await client.PostAsync("https://oauth2.googleapis.com/token", content);
        response.EnsureSuccessStatusCode();
        var newTokenResponse = await response.Content.ReadFromJsonAsync<TokenResponse>();
    
        // 更新数据库中的access token和过期时间
        _userTokenService.UpdateUserTokens(userId, newTokenResponse.AccessToken, userTokens.RefreshToken, DateTime.UtcNow.AddSeconds(newTokenResponse.ExpiresIn));
    
        return newTokenResponse.AccessToken;
    }
    

    在Index方法中,用上述方法获取有效token后再调用API:

    var accessToken = await GetValidAccessToken(userId);
    if (!string.IsNullOrEmpty(accessToken))
    {
        var client = new HttpClient();
        var request = new HttpRequestMessage(HttpMethod.Post, "https://photoslibrary.googleapis.com/v1/mediaItems:search?pageSize=100");
        request.Headers.Add("Authorization", "Bearer " + accessToken);
        var content = new StringContent("", null, "text/plain");
        request.Content = content;
        var response = await client.SendAsync(request);
        response.EnsureSuccessStatusCode();
    
        responseString = await response.Content.ReadAsStringAsync();
        _lookupTableModelService.InsertLookupTable("GooglePhotosJson", responseString);
    }
    

关于“后端自动完成授权”的说明

普通Google个人账户无法实现完全无用户交互的后端授权——这是Google的安全机制,要求用户亲自登录并授予权限以保护个人数据。仅需一次初始授权后,即可通过refresh token长期维持访问,无需用户再次操作。

若你的用户是G Suite/Google Workspace域内账号,可使用服务账号结合域范围委派实现后端静默授权:

  • 在Google Cloud控制台启用服务账号,为其授予Photos Library API相关权限
  • 在Workspace admin控制台为服务账号配置域范围委派,允许它代表域内用户访问Photos Library API
  • 此方案仅适用于Workspace域用户,普通个人账户无法使用。

代码修正总结

  • 确保授权URL包含access_type=offline(你已实现)
  • 首次授权后将refresh token持久化到数据库
  • 每次调用API前检查access token有效性,过期则用refresh token刷新
  • 普通个人账户无法跳过初始用户授权,Workspace用户可通过域范围委派实现静默访问

内容的提问来源于stack exchange,提问作者Willê Nel

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 05:54:52