ASP.NET Core 6.0 Razor Pages:如何将登录用户信息存入Session?
解决方案
针对你的需求,有几种简便且可靠的实现方式,无需强制所有页面模型继承基类:
1. 利用OpenID Connect认证成功事件钩子(推荐)
在Azure AD的OpenID Connect认证流程中,OnTokenValidated事件会在用户登录成功、令牌验证通过后触发,此时HttpContext.User已完全可用,是将用户信息存入Session的最佳时机。
实现步骤:
首先确保Session中间件已正确注册(顺序很重要),然后在OIDC配置中添加事件处理:
// Program.cs var builder = WebApplication.CreateBuilder(args); // 注册Session服务 builder.Services.AddSession(options => { options.IdleTimeout = TimeSpan.FromHours(1); // 根据需求设置过期时间 options.Cookie.HttpOnly = true; options.Cookie.IsEssential = true; }); // 添加Razor Pages和认证服务 builder.Services.AddRazorPages(); builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect(options => { builder.Configuration.Bind("AzureAd", options); // 注册令牌验证成功后的事件 options.Events = new OpenIdConnectEvents { OnTokenValidated = async context => { // 提取userPrincipalName声明 var userPrincipalName = context.Principal.FindFirstValue(ClaimTypes.UserPrincipalName); if (!string.IsNullOrEmpty(userPrincipalName)) { // 将信息存入Session await context.HttpContext.Session.SetString("UserPrincipalName", userPrincipalName); // 可按需存入其他声明,比如DisplayName等 var displayName = context.Principal.FindFirstValue(ClaimTypes.DisplayName); if (!string.IsNullOrEmpty(displayName)) { await context.HttpContext.Session.SetString("DisplayName", displayName); } } } }; }); var app = builder.Build(); // 中间件顺序:Session必须在Authentication之前 app.UseSession(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.Run();
优点:仅在用户登录成功时执行一次,性能开销小,逻辑直接对应认证成功的时机。
2. 使用全局Razor Pages页面过滤器
Razor Pages支持页面过滤器(IAsyncPageFilter/IPageFilter),可以全局注册一个过滤器,在每次页面请求时检查用户是否已认证,若Session中无用户信息则自动存入。
实现步骤:
- 创建全局页面过滤器类:
public class UserSessionPageFilter : IAsyncPageFilter { public async Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) { // 此阶段无需处理,留空即可 } public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) { var httpContext = context.HttpContext; // 检查用户是否已认证,且Session中未存储用户信息 if (httpContext.User.Identity.IsAuthenticated && string.IsNullOrEmpty(httpContext.Session.GetString("UserPrincipalName"))) { var userPrincipalName = httpContext.User.FindFirstValue(ClaimTypes.UserPrincipalName); if (!string.IsNullOrEmpty(userPrincipalName)) { await httpContext.Session.SetString("UserPrincipalName", userPrincipalName); } } // 继续执行后续处理 await next(); } }
- 在Program.cs中注册全局过滤器:
builder.Services.AddScoped<IAsyncPageFilter, UserSessionPageFilter>();
优点:自动处理Session过期或重新登录的场景,确保每次请求时Session中都有最新的用户信息;无需修改现有页面模型。
3. 不推荐的方案:基类继承
虽然创建基类让所有PageModel继承可以实现需求,但这种方式会增加代码耦合度,后续维护和扩展成本更高,不如上述两种方案灵活简便。
内容的提问来源于stack exchange,提问作者marc_s
相关产品推荐
相关产品推荐

