ASP.NET Core中自定义用户授权代码的合适放置位置咨询
ASP.NET Core 中替代Session_OnStart的授权校验方案
从ASP.NET Framework转到Core后,确实没有直接对应的Session_OnStart事件,但可以通过以下几种方案实现每个用户会话仅加载一次用户配置的需求,按场景推荐:
1. 自定义会话中间件(最贴近原逻辑)
这是最接近Session_OnStart的实现方式,通过中间件拦截请求,判断当前会话是否首次初始化,再执行配置查询。
- 核心逻辑:检查会话可用性+自定义标记,确保仅在会话首次创建时执行一次查询
- 代码示例:
public class UserConfigMiddleware { private readonly RequestDelegate _next; public UserConfigMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context, IUserConfigRepository configRepo) { // 会话可用且未加载过配置时执行查询 if (context.Session.IsAvailable && !context.Session.GetBoolean("HasLoadedUserConfig")) { var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier); var userConfig = await configRepo.GetUserConfigAsync(userId); // 将配置存入Session,后续请求直接读取 context.Session.SetString("UserPermissions", JsonSerializer.Serialize(userConfig.Permissions)); context.Session.SetBoolean("HasLoadedUserConfig", true); } await _next(context); } } // 注册中间件注意顺序:必须放在UseSession之后 app.UseSession(); app.UseMiddleware<UserConfigMiddleware>();
2. 自定义授权处理器(权限校验深度绑定)
如果你的配置直接关联权限校验,可以把加载逻辑嵌入授权处理器,首次校验时自动加载,后续复用缓存数据。
- 核心逻辑:在授权需求处理时检查配置是否已加载,未加载则查询,之后用Session缓存
- 代码示例:
// 自定义授权需求 public class UserPermissionRequirement : IAuthorizationRequirement { public string RequiredPermission { get; } public UserPermissionRequirement(string requiredPermission) => RequiredPermission = requiredPermission; } // 授权处理器 public class UserPermissionHandler : AuthorizationHandler<UserPermissionRequirement> { private readonly IHttpContextAccessor _httpContextAccessor; private readonly IUserConfigRepository _configRepo; public UserPermissionHandler(IHttpContextAccessor httpContextAccessor, IUserConfigRepository configRepo) { _httpContextAccessor = httpContextAccessor; _configRepo = configRepo; } protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, UserPermissionRequirement requirement) { var session = _httpContextAccessor.HttpContext.Session; if (!session.GetBoolean("HasLoadedUserConfig")) { var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier); var userConfig = await _configRepo.GetUserConfigAsync(userId); session.SetString("UserPermissions", JsonSerializer.Serialize(userConfig.Permissions)); session.SetBoolean("HasLoadedUserConfig", true); } var permissions = JsonSerializer.Deserialize<List<string>>(session.GetString("UserPermissions")); if (permissions?.Contains(requirement.RequiredPermission) == true) { context.Succeed(requirement); } } } // 注册服务 services.AddAuthorization(options => { options.AddPolicy("CanAccessAdmin", policy => policy.Requirements.Add(new UserPermissionRequirement("AdminAccess"))); }); services.AddScoped<IAuthorizationHandler, UserPermissionHandler>();
3. 登录成功后加载(适合有明确登录流程的场景)
如果应用有独立的登录页面,可以在用户登录成功后立即查询配置,直接存入Session或Claims,避免后续请求的拦截判断。
- 核心逻辑:登录验证通过后,同步执行配置查询并缓存
- 代码示例:
[HttpPost] public async Task<IActionResult> Login(LoginModel model) { var user = await _userManager.FindByEmailAsync(model.Email); var signInResult = await _signInManager.PasswordSignInAsync(user, model.Password, model.RememberMe, lockoutOnFailure: false); if (signInResult.Succeeded) { // 登录成功后加载用户配置 var userConfig = await _configRepo.GetUserConfigAsync(user.Id); HttpContext.Session.SetString("UserPermissions", JsonSerializer.Serialize(userConfig.Permissions)); return RedirectToAction("Index", "Home"); } ModelState.AddModelError(string.Empty, "登录失败,请检查账号密码"); return View(model); }
注意事项
- 所有方案都依赖Session,确保已在
Program.cs中注册Session服务:services.AddSession(); - 若用户配置可能在会话期间更新,需额外添加刷新逻辑(比如增加一个刷新接口,清除会话标记后重新加载)
内容的提问来源于stack exchange,提问作者Mark C
相关产品推荐
相关产品推荐

