asp.net mvc关于提供的防伪标记适用于其他基于声明的用户,而不适用于当前用户错误的处理

本文主要是介绍asp.net mvc关于提供的防伪标记适用于其他基于声明的用户,而不适用于当前用户错误的处理,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

问题环境:用户在一个浏览器中同时打开两个登录页面,在第一个页面中输入用户名和密码登录成功,然后跳转到另一页面。但是在另一个页面中再次输入用户名和密码重新登录后却出现了:

提供的防伪标记适用于其他基于声明的用户,而不适用于当前用户。

说明:执行当前 Web 请求期间,出现未经处理的异常。请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息。

异常详细信息: System.Web.Mvc.HttpAntiForgeryException: 提供的防伪标记适用于其他基于声明的用户,而不适用于当前用户。


登录的login页面的action使用了ValidateAntiForgeryToken


出现这种问题有人也许会说是由于用户不按牌理出牌,你说你登录还打开两个登录页。而且你已经登录成功了,你还再次登录,不是找事么。但是你不能要求用户不能打开两个登录页面。出现这种问题或许是用户在进行操作的时候打开了很多页面。刚好有两个都是登录页面。他操作一个登录成功后或许有其他事情去其他页面处理事情了。然后回来顺手打开了未登录的登录页。以为自己没登录,顺手又进行了此项操作,而导致了此问题的出现。


在网上搜索此类问题。大多都是搜索到的是用户采用ajax登录,先是匿名登录。登录成功后没跳转,再次点击登录就会出现此错误。给出的解决办法是其一,你可以刷新antiforgery token然后传到前台,再次提交的时候替换此token;第二种解决办法是用form表单的形式提交用户名和密码,登录成功后跳转到其他页面来规避此问题。这和我碰到的问题不一样。

那应该怎么解决此问题呢,我想到了mvc提供的过滤器,因为ValidateAntiForgeryToken其本质也是一个过滤器,如果我写一个过滤器,在ValidateAntiForgeryToken之前执行,先判断用户是否已经登录,如果未登录,不做任何处理。如果已经登录,直接跳转到登录成功后应该跳转的页面,应该能解决此问题。有了这个思路。在网上搜索了下过滤器的执行顺序。有两条规则:

第一条规则:

IAuthorizationFilter(OnAuthorization)----->IActionFilter(OnActionExecuting)---->控制器Action---->IActionFilter(OnActionExecuted) ---->IResultFilter(OnResultExecuting)---->视图---->IResultFilter(OnResultExecuted)

第二条规则:

[Filter1]
[Filter2]
public ActionResult DoAction(){}

上面的这个 Action 配置有两个过滤器:Filter1、Filter2。这两个过滤器也肯定是有执行顺序的,但是它们的顺序与直观的上下顺序正好相反:先执行 Filter2,再执行 Filter1。

由于我的是在login action上使用的过滤器。先参考第二条规则。重写ActionFilter,代码如下:

 public class LoginActionFilterAttribute : System.Web.Mvc.ActionFilterAttribute{public override void OnActionExecuted(ActionExecutedContext filterContext){base.OnActionExecuted(filterContext);}public override void OnActionExecuting(ActionExecutingContext filterContext){string userName = HttpContext.Current.User.Identity.Name;EventLog.WriteLog(userName);if (userName != null){if (HttpContext.Current.User.IsInRole("person")){EventLog.WriteLog("a");HttpContext.Current.Response.Redirect("/person", true);}else if (HttpContext.Current.User.IsInRole("hr")){EventLog.WriteLog("b");HttpContext.Current.Response.Redirect("/hr", true);}else if (HttpContext.Current.User.IsInRole("admin")){EventLog.WriteLog("c");HttpContext.Current.Response.Redirect("/admin/home", true);}}base.OnActionExecuting(filterContext);}
}


然后把这个附加到login上:
//  POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[LoginActionFilter]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
结果问题依旧。把
ValidateAntiForgeryToken和LoginActionFilter调换下次序,仍然是一样的结果。通过事务日志EventLog.WriteLog输出来看,在登录成功的页面只输出了Username值,是个空值。其他的分支eventLog都没输出。而在登录出现提供的防伪标记适用于其他基于声明的用户,而不适用于当前用户这个页面,连用户名这个空值信息都没输出,这意味着ValidateAntiForgeryToken始终是先于LoginActionFilter来执行的。与第二条规则不符。分析应该是第一条规则是第一顺序,然后具体到action上才会实行第二条规则。而由于在网上搜索不到ValidateAntiForgeryToken这个过滤器在第一规则中的位置,还不清楚他在第一规则中的位置。由于这次尝试失败,打算使用AuthorizationFilter.因此创建了下面的类:
public class LoginAuthorizeAttribute : AuthorizeAttribute{protected override bool AuthorizeCore(HttpContextBase httpContext){if (httpContext == null){throw new ArgumentNullException("HttpContext");}return base.AuthorizeCore(httpContext);}public override void OnAuthorization(AuthorizationContext filterContext){string userName = filterContext.HttpContext.User.Identity.Name;EventLog.WriteLog(userName);if (userName != null){LMIdentityDbContext db = new LMIdentityDbContext();var user = db.Users.Find(filterContext.HttpContext.User.Identity.GetUserId());EventLog.WriteLog(user + "");if (user != null){if (user.Role == "person"){EventLog.WriteLog("a");filterContext.Result = new RedirectResult("/person"true);}else if (user.Role == "hr"){EventLog.WriteLog("b");filterContext.Result = new RedirectResult("/hr"true);}else if (user.Role == "admin"){EventLog.WriteLog("c");filterContext.Result = new RedirectResult("/admin/home"true);}}}base.OnAuthorization(filterContext);}}
login action代码如下:
//  POST: /Account/Login[HttpPost][AllowAnonymous][LoginAuthorize][ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
结果为:当登录成功时,eventlog.WriteLog的username输出为空。而在登录出现提供的防伪标记适用于其他基于声明的用户,而不适用于当前用户时,则eventlog.WriteLog没有任何提示。调整login action代码顺序如下:
//  POST: /Account/Login[HttpPost][AllowAnonymous][ValidateAntiForgeryToken][LoginAuthorize]public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
第一个打开登录页面,登录成功,第二个打开的登录页面,再次输入用户名和密码登录,登录成功,没有任何错误提示。查看日志文件的输出值。看到了EventLog.WriteLog("a");这个的输出。这意味着首先执行了LoginAuthorrize,经过判断,由于已经登录,直接跳转到登录页面,而后续的ValidateAntiForgeryToken这个过滤器不再执行。
总结:
第一条规则应该是这样:
IAuthorizationFilter(OnAuthorization)---->ValidateAntiForgeryToken----->IActionFilter(OnActionExecuting)---->控制器Action---->IActionFilter(OnActionExecuted) ---->IResultFilter(OnResultExecuting)---->视图---->IResultFilter(OnResultExecuted)

 
 

即在这个规则中,ValidateAntiForgeryToken过滤器始终先于ActionFilter过滤器执行。而具体到action方法上。如果重写的AutorizationFilter在ValidateAntiForgeryToken上面,则先执行ValidateAntiForgeryToken过滤器,否则则先执行重写的AutorizationFilter的规则。具体到此问题上,应是

//  POST: /Account/Login[HttpPost][AllowAnonymous][ValidateAntiForgeryToken][LoginAuthorize]public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
使用这个顺序才能正确解决此问题


重要补充:
由于又做了一个项目,在此实践过程中,发觉以上的处理方法还是有些问题。

其一,LoginAuthorizeAttribute类的OnAuthorization方法中,base.OnAuthorization(filterContext);应放置在方法的顶部,而不是最下面,原因在http://blog.csdn.net/sxf359/article/details/54928841 这篇文章中我已经介绍,这里不再细说。

其二,LoginAuthorizeAttribute在写的时候是判断是否已经登录,如果已经登录,则直接跳转到登录页面,但是这个没考虑到换账号登录的情况,如果换了账号登录,则用上面写的方法,它判断你已经登录了,而直接跳转到登录后的页面,但这不是你想要的,因为账号已经换了。因此LoginAuthorizeAttribute这个方法要改写下,在这个方法中判断有没有ValidateAntiForgeryToken的提供的防伪标记适用于其他基于声明的用户,而不适用于当前用户错误,如果有,则表示已经登录过,这个时候再次跳到登录页面,让其重新登录,这样一是避免了错误直接暴露给了用户,另一个也能防止用户换账号登录的情况。更改后的方法如下:

public class LoginAuthorizeAttribute : AuthorizeAttribute{protected override bool AuthorizeCore(HttpContextBase httpContext){if (httpContext == null){throw new ArgumentNullException("HttpContext");}return base.AuthorizeCore(httpContext);}public override void OnAuthorization(AuthorizationContext filterContext){base.OnAuthorization(filterContext);if (filterContext == null){throw new ArgumentNullException("filterContext");}if (filterContext.HttpContext.User != null &&filterContext.HttpContext.User.Identity != null){//EventLog.WriteLog("start4");try{//EventLog.WriteLog("start5");System.Web.Helpers.AntiForgery.Validate();//EventLog.WriteLog("start7");}catch(Exception ex){EventLog.WriteLog("start6");filterContext.Result = new RedirectResult("/account/login"true);return;//throw;}}else{EventLog.WriteLog("start3");}}}
其三、在上面总结的规则中,ACTION上面过滤器的规则执行顺序是自下而上执行。但经过两个项目的实践,其实不是这么回事,它有时是自下而上执行,但有时是自上而下执行。而且这个还不是随机的。有段时间自下而上顺序起作用,有段时间又变成了自上而下执行。这真令人崩溃。这就导致了

你以为这个错误已经避免了,但错误日志中同样的错误过段时间又出现了。我采取的解决办法是,你既然有时自下而上,有时自上而下,那我在ValidateAntiForgeryToken上下都放一个登陆效验过滤器,以避免这种情况。

[HttpPost]
[AllowAnonymous]
[LoginAuthorize]
[ValidateAntiForgeryToken]
[LoginAuthorize]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)

更改后就是这个样子

这种处理是丑陋的,同一方法执行两次有点不能容忍,若想避免这种情况,或许要自己写一个类似的ValidateAntiForgeryToken的方法,来替代vs的ValidateAntiForgeryToken方法,以便彻底解决此问题。先这样,以后有空了写一个这样得方法

现在新写了篇文章 自写过滤器替代ValidateAntiForgeryToken解决asp.net mvc关于提供的防伪标记适用于其他基于声明的用户,而不适用于当前用户的错误 , 有兴趣的可以看看

 

这篇关于asp.net mvc关于提供的防伪标记适用于其他基于声明的用户,而不适用于当前用户错误的处理的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/768221

相关文章

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

Springboot处理跨域的实现方式(附Demo)

《Springboot处理跨域的实现方式(附Demo)》:本文主要介绍Springboot处理跨域的实现方式(附Demo),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不... 目录Springboot处理跨域的方式1. 基本知识2. @CrossOrigin3. 全局跨域设置4.

python+opencv处理颜色之将目标颜色转换实例代码

《python+opencv处理颜色之将目标颜色转换实例代码》OpenCV是一个的跨平台计算机视觉库,可以运行在Linux、Windows和MacOS操作系统上,:本文主要介绍python+ope... 目录下面是代码+ 效果 + 解释转HSV: 关于颜色总是要转HSV的掩膜再标注总结 目标:将红色的部分滤

Python实现自动化接收与处理手机验证码

《Python实现自动化接收与处理手机验证码》在移动互联网时代,短信验证码已成为身份验证、账号注册等环节的重要安全手段,本文将介绍如何利用Python实现验证码的自动接收,识别与转发,需要的可以参考下... 目录引言一、准备工作1.1 硬件与软件需求1.2 环境配置二、核心功能实现2.1 短信监听与获取2.

mysql如何查看当前连接数

《mysql如何查看当前连接数》:本文主要介绍mysql如何查看当前连接数问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录mysql查看当前连接数查看mysql数据库允许最大连接数总结mysql查看当前连接数查看当前连接数SHOW STATUS LIKE

Python使用date模块进行日期处理的终极指南

《Python使用date模块进行日期处理的终极指南》在处理与时间相关的数据时,Python的date模块是开发者最趁手的工具之一,本文将用通俗的语言,结合真实案例,带您掌握date模块的六大核心功能... 目录引言一、date模块的核心功能1.1 日期表示1.2 日期计算1.3 日期比较二、六大常用方法详

Python中ModuleNotFoundError: No module named ‘timm’的错误解决

《Python中ModuleNotFoundError:Nomodulenamed‘timm’的错误解决》本文主要介绍了Python中ModuleNotFoundError:Nomodulen... 目录一、引言二、错误原因分析三、解决办法1.安装timm模块2. 检查python环境3. 解决安装路径问题