ASP.NET Core 2.0如何实现加载页面时强制改密并防绕过?
解决ASP.NET Core 2.0强制用户改密码防绕过的问题
这问题我之前做项目的时候也碰到过,要彻底堵住用户通过URL绕过改密流程的漏洞,核心就是在请求处理的全局环节自动检查用户的IsChangePassword标记,不让任何未改密的用户访问其他页面。下面给你两个靠谱的实现方案,都是ASP.NET Core 2.0里能用的:
方案一:使用全局中间件(推荐,覆盖所有请求)
中间件是ASP.NET Core请求管道的核心组件,能拦截所有进来的请求,非常适合做这种全局检查。
1. 创建自定义中间件类
public class ForceChangePasswordMiddleware { private readonly RequestDelegate _next; public ForceChangePasswordMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext context, UserManager<ApplicationUser> userManager) { // 跳过静态文件、登录页、改密页和未认证的请求 var path = context.Request.Path.Value.ToLower(); if (path.StartsWith("/css/") || path.StartsWith("/js/") || path.StartsWith("/images/") || path == "/account/login" || path == "/account/changepassword" || !context.User.Identity.IsAuthenticated) { await _next(context); return; } // 获取当前登录用户 var user = await userManager.GetUserAsync(context.User); if (user != null && user.IsChangePassword) { // 重定向到改密页面,带上原路径方便改完后跳转(可选) context.Response.Redirect("/Account/ChangePassword?returnUrl=" + Uri.EscapeDataString(context.Request.Path)); return; } await _next(context); } }
2. 在Startup.cs里注册中间件
要把这个中间件放在认证中间件之后(app.UseAuthentication()之后),不然获取不到用户信息:
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { // ...其他中间件(比如静态文件、错误页) app.UseAuthentication(); // 注册强制改密中间件 app.UseMiddleware<ForceChangePasswordMiddleware>(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); }
方案二:使用全局Action过滤器(适合MVC场景)
如果你的项目主要是MVC控制器,用Action过滤器也能实现,只拦截控制器的Action请求,不会影响静态文件等资源。
1. 创建自定义Action过滤器
public class ForceChangePasswordFilter : IAsyncActionFilter { private readonly UserManager<ApplicationUser> _userManager; public ForceChangePasswordFilter(UserManager<ApplicationUser> userManager) { _userManager = userManager; } public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // 跳过登录、改密Action,以及未认证用户 var controllerName = context.RouteData.Values["controller"]?.ToString().ToLower(); var actionName = context.RouteData.Values["action"]?.ToString().ToLower(); if ((controllerName == "account" && (actionName == "login" || actionName == "changepassword")) || !context.HttpContext.User.Identity.IsAuthenticated) { await next(); return; } // 获取当前登录用户 var user = await _userManager.GetUserAsync(context.HttpContext.User); if (user != null && user.IsChangePassword) { // 重定向到改密页 context.Result = new RedirectToActionResult("ChangePassword", "Account", new { returnUrl = context.HttpContext.Request.Path }); return; } await next(); } }
2. 在Startup.cs里注册全局过滤器
public void ConfigureServices(IServiceCollection services) { // ...其他服务配置(比如AddDbContext、AddIdentity) services.AddMvc(options => { // 添加全局Action过滤器 options.Filters.Add(typeof(ForceChangePasswordFilter)); }); }
关键注意点
- 避免死循环:一定要排除改密页面本身的请求,不然用户进入改密页后会被再次重定向,陷入无限循环。
- 静态文件跳过:中间件里记得跳过css、js、图片等静态资源,不然会导致页面样式加载异常,也没必要检查这些请求。
- 异步优先:过滤器尽量用异步实现(
IAsyncActionFilter),比同步等待结果的方式更符合ASP.NET Core的设计理念,性能也更好。
内容的提问来源于stack exchange,提问作者user369484
相关产品推荐
相关产品推荐

