ASP.NET Core MVC中如何获取Google Photos Library API刷新令牌
核心问题解决:获取并使用刷新令牌
Google Photos Library API 支持刷新令牌,你当前的问题是未正确保存和使用刷新令牌。只要授权时指定access_type=offline(你已在URL中加入该参数),首次授权后Google会返回refresh_token,后续用它即可刷新access_token,无需用户再次手动授权。
修正步骤:
持久化保存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"); }用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

