部署至Azure App Service的.NET项目Graph SDK令牌过期问题排查
问题:Azure App Service部署后访问令牌无法刷新,构造函数未触发
将.NET Web应用部署到Azure App Service后,访问令牌1小时过期后无法自动刷新,抛出错误:
ODataError: Lifetime validation failed, the token is expired.
同时发现Invite.cshtml.cs的构造函数在Azure环境中未被调用(本地运行正常),推测这是令牌无法刷新的原因;在OnPostAsync方法中调用_graphServiceClient.Me.GetAsync()时立即触发上述错误。
Invite.cshtml.cs 代码
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.Graph; using Microsoft.Graph.Models; using Microsoft.Identity.Web; using System.Text; using System.Text.Json; using System.ComponentModel.DataAnnotations; using Azure.Identity; namespace Test_Web_App.Pages { [AuthorizeForScopes(ScopeKeySection = "MicrosoftGraph:Scopes")] public class InviteModel : PageModel { private readonly GraphServiceClient _graphServiceClient; private static ClientSecretCredential? _clientSecretCredential; private static GraphServiceClient? _appClient; private readonly HttpClient _httpClient; private readonly ILogger<IndexModel> _logger; private readonly IConfiguration _configuration; private readonly IHttpContextAccessor _httpContextAccessor; public InviteModel(ILogger<IndexModel> logger, IConfiguration configuration, GraphServiceClient graphServiceClient, HttpClient httpClient, IHttpContextAccessor httpContextAccessor) { _logger = logger; _graphServiceClient = graphServiceClient; _httpClient = httpClient; _configuration = configuration; _httpContextAccessor = httpContextAccessor; } public async Task<IActionResult> OnPostAsync() { var user = await _graphServiceClient.Me.GetAsync(); // 省略其他代码 } } }
Program.cs 代码
var builder = WebApplication.CreateBuilder(args); var initialScopes = builder.Configuration["AzureAd:Scopes"]?.Split(' ') ?? builder.Configuration["MicrosoftGraph:Scopes"]?.Split(' '); // Add services to the container. builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApp(options => { builder.Configuration.Bind("AzureAd", options); options.SaveTokens = true; // Ensure tokens are saved }) .EnableTokenAcquisitionToCallDownstreamApi(initialScopes) .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph")) .AddInMemoryTokenCaches().AddMicrosoftGraph(); // 此处存在重复注册 //Named HttpClient with IHttpClientFactory builder.Services.AddTransient<GraphAuthorizationMessageHandler>(); builder.Services.AddHttpClient("GraphAPI", client => client.BaseAddress = new Uri( builder.Configuration.GetSection("MicrosoftGraph")["BaseUrl"] ?? string.Empty)) .AddHttpMessageHandler<GraphAuthorizationMessageHandler>(); builder.Services.AddScoped(sp => sp.GetService<IHttpClientFactory>().CreateClient("GraphAPI")); builder.Services.AddHttpContextAccessor(); // Add this line to register IHttpContextAccessor builder.Services.AddAuthorization(options => { // By default, all incoming requests will be authorized according to the default policy. options.FallbackPolicy = options.DefaultPolicy; }); builder.Services.AddRazorPages() .AddMicrosoftIdentityUI(); var app = builder.Build(); // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios. app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.MapControllers(); app.Run();
排查与修复建议
1. 解决构造函数未触发问题
- 检查
Invite.cshtml页面的模型绑定:确保页面顶部声明@model Test_Web_App.Pages.InviteModel,无拼写错误。 - 确认页面请求路径与模型对应:比如页面是否位于
Pages/Invite.cshtml,路由是否正确映射。
2. 修复令牌刷新与GraphServiceClient注册问题
- 移除重复的
AddMicrosoftGraph()调用:Program.cs中连续调用了两次AddMicrosoftGraph(),这会导致GraphServiceClient实例冲突,修改后代码如下:.EnableTokenAcquisitionToCallDownstreamApi(initialScopes) .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph")) .AddInMemoryTokenCaches(); - 替换内存令牌缓存为分布式缓存:Azure App Service多实例场景下,内存缓存无法跨实例共享令牌,导致刷新失败。可改用Redis或SQL Server分布式缓存,示例代码:
// 替换原AddInMemoryTokenCaches() .AddDistributedTokenCaches() .AddStackExchangeRedisCache(options => { options.Configuration = builder.Configuration["Redis:ConnectionString"]; }); - 确认refresh token获取权限:在Azure AD应用注册中,确保已勾选
offline_access权限,这是获取refresh token的必要条件,否则无法自动刷新令牌。
3. 确保用户上下文有效性
- 在
OnPostAsync方法开头添加认证检查:public async Task<IActionResult> OnPostAsync() { if (!User.Identity.IsAuthenticated) { return Challenge(); } var user = await _graphServiceClient.Me.GetAsync(); // 省略其他代码 } - 避免混用应用权限与用户委托权限:静态的
_appClient使用ClientSecretCredential(应用权限),而_graphServiceClient是用户委托权限,两者上下文独立,不要在同一逻辑中混用。
内容的提问来源于stack exchange,提问作者RMM
相关产品推荐
相关产品推荐

