IdentityServer4外部认证令牌:如何获取并维护Google令牌以离线调用API
嘿,你已经搞定了IdentityServer4和Google认证的基础部分,接下来要解决的是离线场景下用存储的令牌调用Google API并更新令牌的问题对吧?我来一步步给你捋清楚怎么做:
一、核心逻辑先搞懂
首先要明确:AspNetUserTokens里存的其实是Google返回的刷新令牌(访问令牌有效期短,IS4默认不会长期存储,只会保留能续期的刷新令牌)。所以你的独立服务核心动作是用这个刷新令牌向Google请求新的访问令牌——而非向IdentityServer4请求,因为IS4在这里是认证中介,真正的令牌颁发方是Google,IS4只是帮你存了Google的刷新令牌。
当然,如果想让IS4统一管理令牌刷新流程(比如加权限校验、日志记录),也可以在IS4里加个自定义端点,让独立服务调用这个端点完成刷新,这样更符合微服务职责划分。
二、直接用存储的刷新令牌调用Google API(无需IS4中转)
如果你的独立服务能直接访问AspNetUserTokens里的刷新令牌,可以直接和Google的令牌端点交互:
- 找对Google的令牌端点:固定为
https://oauth2.googleapis.com/token - 构造刷新请求:
向这个端点POST以下application/x-www-form-urlencoded格式的参数:grant_type=refresh_tokenclient_id=你的Google OAuth客户端IDclient_secret=你的Google OAuth客户端密钥refresh_token=从AspNetUserTokens中取出的对应用户的Google刷新令牌
- 处理响应并更新存储:
Google会返回新的访问令牌、过期时长(expires_in,单位秒),部分场景下还会返回新的刷新令牌。你需要把新的访问令牌、计算好的过期时间戳(当前时间+expires_in秒)更新回AspNetUserTokens,若有新刷新令牌也要同步更新。
示例C#代码:
using System.Net.Http; using System.Threading.Tasks; using System.Collections.Generic; using System.Text.Json; public async Task<GoogleTokenResponse> RefreshGoogleToken(string refreshToken, string googleClientId, string googleClientSecret) { using var httpClient = new HttpClient(); var formData = new Dictionary<string, string> { ["grant_type"] = "refresh_token", ["client_id"] = googleClientId, ["client_secret"] = googleClientSecret, ["refresh_token"] = refreshToken }; var response = await httpClient.PostAsync("https://oauth2.googleapis.com/token", new FormUrlEncodedContent(formData)); response.EnsureSuccessStatusCode(); var tokenResponse = await response.Content.ReadFromJsonAsync<GoogleTokenResponse>(); return tokenResponse; } // 定义响应模型适配Google返回格式 public class GoogleTokenResponse { public string access_token { get; set; } public int expires_in { get; set; } public string token_type { get; set; } public string refresh_token { get; set; } // 可选,Google不一定每次都返回新的 }
三、通过IdentityServer4自定义端点实现令牌刷新(推荐)
如果想让IS4统一处理刷新逻辑(比如校验调用方权限、记录操作日志),可以在IS4里加个自定义端点:
- 在IS4项目中添加自定义端点:
以.NET 6+为例,在Program.cs的端点配置里添加:app.UseEndpoints(endpoints => { // 保留原有的IS4端点配置 endpoints.MapIdentityServer(); // 添加自定义的Google令牌刷新端点 endpoints.MapPost("/api/refresh-google-token", async context => { // 1. 校验调用方合法性:比如验证独立服务的API密钥 var apiKey = context.Request.Headers["X-API-Key"].FirstOrDefault(); if (!IsValidApiKey(apiKey)) // 这里需要你实现自己的API密钥校验逻辑 { context.Response.StatusCode = StatusCodes.Status401Unauthorized; await context.Response.WriteAsync("Invalid API key"); return; } // 2. 获取请求中的用户ID var requestBody = await context.Request.ReadFromJsonAsync<RefreshTokenRequest>(); var userManager = context.RequestServices.GetRequiredService<UserManager<ApplicationUser>>(); var user = await userManager.FindByIdAsync(requestBody.UserId); if (user == null) { context.Response.StatusCode = StatusCodes.Status404NotFound; await context.Response.WriteAsync("User not found"); return; } // 3. 从AspNetUserTokens中取出Google刷新令牌 var googleRefreshToken = await userManager.GetAuthenticationTokenAsync(user, "Google", "refresh_token"); if (string.IsNullOrEmpty(googleRefreshToken)) { context.Response.StatusCode = StatusCodes.Status400BadRequest; await context.Response.WriteAsync("No Google refresh token found for user"); return; } // 4. 调用Google的令牌端点刷新令牌(复用上面的RefreshGoogleToken方法) var config = context.RequestServices.GetRequiredService<IConfiguration>(); var googleClientId = config["Authentication:Google:ClientId"]; var googleClientSecret = config["Authentication:Google:ClientSecret"]; var tokenResponse = await RefreshGoogleToken(googleRefreshToken, googleClientId, googleClientSecret); // 5. 更新AspNetUserTokens中的令牌信息 await userManager.SetAuthenticationTokenAsync(user, "Google", "access_token", tokenResponse.access_token); var expiresAt = DateTime.UtcNow.AddSeconds(tokenResponse.expires_in); await userManager.SetAuthenticationTokenAsync(user, "Google", "expires_at", expiresAt.ToString("o")); // 如果返回了新的刷新令牌,同步更新 if (!string.IsNullOrEmpty(tokenResponse.refresh_token)) { await userManager.SetAuthenticationTokenAsync(user, "Google", "refresh_token", tokenResponse.refresh_token); } // 6. 返回新令牌信息给独立服务 await context.Response.WriteAsJsonAsync(new { AccessToken = tokenResponse.access_token, ExpiresAt = expiresAt, RefreshToken = tokenResponse.refresh_token ?? googleRefreshToken }); }); });
// 定义请求模型
public class RefreshTokenRequest
{
public string UserId { get; set; }
}
2. **独立服务调用IS4的自定义端点**: 独立服务只需向IS4的`/api/refresh-google-token`端点POST请求,带上合法的API密钥和用户ID,就能获取新的Google访问令牌,之后用这个令牌调用Google API即可。 # 四、关键注意事项 - **权限校验不能少**:自定义端点一定要加调用方校验(比如API密钥、JWT令牌验证),避免未授权访问带来的安全风险。 - **敏感信息要加密**:AspNetUserTokens里的刷新令牌是敏感数据,IS4默认已加密存储,但要确保数据库本身的安全性。 - **处理刷新失败场景**:Google的刷新令牌可能因用户修改密码、撤销权限等原因失效,代码要捕获这类错误(比如401响应),提示用户重新登录。 - **提前检查过期时间**:存储访问令牌的过期时间,下次调用Google API前先判断是否过期,避免无效请求。 内容的提问来源于stack exchange,提问作者ChickenMilkBomb
相关产品推荐
相关产品推荐

