Blazor基于Entra MFA登录的会话时长设置问题
解决Blazor Entra MFA会话超时问题
你遇到的1小时强制登出是Azure AD默认的令牌过期机制导致的——当前你用的是JwtBearer无状态认证方案,代码里配置的ApplicationCookie和Session完全不会影响JWT的有效期,因为JWT的过期时间由令牌本身的exp字段决定,默认就是1小时。
核心解决步骤
1. 调整Azure AD应用的Token过期时间
直接在Entra ID控制台修改应用注册的令牌有效期:
- 进入Entra ID → 应用注册 → 找到你的Blazor应用
- 切换到「令牌配置」页面
- 点击「添加配置」,分别设置访问令牌和ID令牌的过期时间(最长可设为24小时)
- 保存后,新生成的Token就会使用这个有效期
2. 配置自动滑动刷新(实现长期会话)
如果需要超过24小时的会话,或者希望用户活跃时自动续期,需要开启Microsoft Identity Web的滑动刷新功能:
在AddMicrosoftIdentityWebApi之后添加JWT事件配置:
builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options => { options.Events = new JwtBearerEvents { OnTokenValidated = context => { var expiresAt = context.SecurityToken.ValidTo; // 当Token剩余有效期不足30分钟时触发刷新 if (expiresAt - DateTime.UtcNow < TimeSpan.FromMinutes(30)) { var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>(); // 替换成你的实际API权限范围 tokenAcquisition.GetAccessTokenForUserAsync(new[] { "https://graph.microsoft.com/User.Read" }) .ContinueWith(task => { if (task.IsCompletedSuccessfully) { context.Properties.UpdateTokenValue("access_token", task.Result); } }); } return Task.CompletedTask; } }; });
同时要确保你的应用注册已添加offline_access权限,这样才能获取刷新令牌用于续期。
3. 清理无效配置
删除代码中对JWT认证无意义的配置,避免混淆:
- 移除
builder.Services.ConfigureApplicationCookie(...) - 移除
builder.Services.AddSession(...)和builder.Services.AddMvc().AddSessionStateTempDataProvider() - 补充遗漏的
app.UseAuthentication()中间件(原代码未添加,会导致认证逻辑不生效)
修改后的完整Program.cs示例
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.ResponseCompression; using Microsoft.EntityFrameworkCore; using Microsoft.Identity.Web; var builder = WebApplication.CreateBuilder(args); // 配置Entra认证 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd")) .EnableTokenAcquisitionToCallDownstreamApi() .AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph")) .AddDistributedTokenCaches() .AddSessionTokenCaches(); // 配置JWT滑动刷新 builder.Services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options => { options.Events = new JwtBearerEvents { OnTokenValidated = context => { var expiresAt = context.SecurityToken.ValidTo; if (expiresAt - DateTime.UtcNow < TimeSpan.FromMinutes(30)) { var tokenAcquisition = context.HttpContext.RequestServices.GetRequiredService<ITokenAcquisition>(); tokenAcquisition.GetAccessTokenForUserAsync(new[] { "https://graph.microsoft.com/User.Read" }) .ContinueWith(task => { if (task.IsCompletedSuccessfully) { context.Properties.UpdateTokenValue("access_token", task.Result); } }); } return Task.CompletedTask; } }; }); builder.Services.AddControllersWithViews(); builder.Services.AddRazorPages(); builder.Services.AddControllers().AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore); builder.Services.AddDbContext<DB>(x => x.UseSqlServer(builder.Configuration.GetConnectionString("DB")) .UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)); var app = builder.Build(); // 管道配置 if (app.Environment.IsDevelopment()) { app.UseWebAssemblyDebugging(); } else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseBlazorFrameworkFiles(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); // 必须添加的认证中间件 app.UseAuthorization(); app.MapRazorPages(); app.MapControllers(); app.MapFallbackToFile("index.html"); app.Run();
内容的提问来源于stack exchange,提问作者jbmintjb
相关产品推荐
相关产品推荐

