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

部署至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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 01:20:23