zoukankan      html  css  js  c++  java
  • 注册登录过程点滴(三):解决MVC3中使用Ajax.BeginForm会重复提交数据的问题

    MVC3这个开发框架还是很受大家欢迎的,我个人也非常喜欢,逢人总要夸奖一番,虽然还有很多地方需要完善,但总体上来说很棒!

    不过,最近碰到一个比较纠结的问题,在点击注册时,为了使用一些友好的前端交互效果,所以使用 Ajax.BeginForm的方式,代码如下:

    @using (Ajax.BeginForm("Register""Account"new AjaxOptions { UpdateTargetId = "ResultPannel", OnBegin = "RegOngoing", OnSuccess = "RegDone" }))
    {...

    分别通过 RegOngoing和 RegDone 执行注册过程中和完成后的前端提示效果。

    但这种方式经常会在数据库中出现重复的数据,分析了一下原因,初步判断应该是注册事件被重复提交了,虽然在后端逻辑层会检测数据是否存在,但其执行过程中本质上是会顺序执行各种逻辑,所以在前一条还没有插入数据库,后一天已经被提交过来被检测的概率是存在的,尤其是如果在数据访问层端有较长的事务性逻辑存在的话,那这种重复提交的概率会更高!

    采取的第一套方法:就是在用户点击后将注册按钮disable掉,并给与友好提示,避免用户去重复点击注册...

    function RegOngoing() {
       $("#Register").attr("disabled"true);
       $("#Register").val("信息提交中...");}

     这在很长一段时间内我们都认为解决了这个问题,可是在月黑分高的一个晚上,重复数据又出现了,这下纠结了,难道是被黑了?

    利用一切可利用的资源,google、baidu、博客园、qq群等等到处找问题根源,都没有很好的结果。最近团队开会,仔细的分析了一下,发觉问题可能还是出在上面分析过的可能性,比如:很多用户有双击的习惯,很多用户有连续按回答的习惯...,只要RegOngoing反应迟钝一点,就还是会存在这种被重复提交注册的概率,虽然概率很小,但问题不能放过去,针对此种可能性,我们对这个注册场景进行了无思考时间的压力测试,发觉果真还是会被重复提交。

    这样,第二套方案应运而生: 采用token预防机制,在Register的action中增加一个基于IP的token策略,并在注册逻辑执行完成后删除token(这里不管是否成功,都必须删除哦,不然你再想注册就有可能不行)。


    [HttpPost]

    public ActionResult Register(RegisterModel reg)
            {
                //防止用户并发提交数据
                string key = StringHelper.GetClientIP();
                if (!string.IsNullOrEmpty(RegTokenHelper.findTokenByKey(key)))
                {
                    return Content("errorSubmit");
                }
                //生成用户Token
                RegTokenHelper.generateToken(key);

                if (IsValidateCode(reg.sCode) == false)
                {
                    //删除Token
                    RegTokenHelper.deleteToken(key);
                    return Content("errorCode");
                }

                ...
                            
                //删除Token
                RegTokenHelper.deleteToken(key);
                return Content(url);
            }

     这里主要是用本机的IP作为本机唯一访问的标识,至于token值可以自己生成,也可以用系统的,我们采用的是guid值。

    至此,这个问题基本上可以告一段落了!但是其实不知道大家发现没有,上述方法中还存在一定的问题,或者注册失败的风险,就是当用户在同一个局域网内并发的进行注册,那是有可能失败的,因为其token是相同的,后者会被认为是已经正在注册!

    这种场景其实也是有解决方法,就是不要使用IP作为key,而采用session。根据我们网站的用户特点,前者发生的概率实在是微乎其微,而且本质上没有什么危害性,所以我们没有采用session,而默认接受这种风险了^_^

     至于大家要追求完美,那就无可非议了! 同时希望MVC的团队能从框架性上去解决这个问题,因为这种场景是在是太多啦!!!

    如果大牛们还有更好的办法,Q我或者留言给我吧 

  • 相关阅读:
    接口和类的关系
    Java9+版本中,Interface的内容
    XSS简介
    上传漏洞(一)
    上传漏洞(二)
    初学Django
    ISCC:Please give me username and password!
    各种密码
    Debian 8.9 搭建wordpress个人博客
    网安相关书籍
  • 原文地址:https://www.cnblogs.com/jivenbest/p/2609929.html
Copyright © 2011-2022 走看看