我所关心的是“将哪些信息存放到数据库中”
一般来说,开发一个系统的过程中,排错的时间往往都要多于编码的时间。而对于维护旧有的项目,特别是由其他人开发的项目时,排错所花的时间就更要多了。
前一段时间维护的 CGI 网站,近日在调试组精细的排查下,列出了N多个问题。有些问题似乎有些匪夷所思,但却又是确实存在的,可忙坏了一帮做开发的兄弟们。我呢,今天上午刚刚听完一个答辩会,下午就接手了一个异常难啃的 BUG。
我们知道,用 ASP 或是 ASP.NET 开发的网站都可以使用一个 Session 的好东西,它可以用来保存与特定用户相关的一些信息,并可以设定一些比如超时之类的数据。但是对于 CGI 程序来说,就没有这个便利性了。而我们这个 CGI 程序,还有一些特殊的要求,比如:要求每一时刻不能同时有两个相同的用户使用系统,并且必须要实现超时限制这么一个功能。
应当说,当年这个系统的设计者还是比较出色的,基本圆满地解决了这两个问题。他的思路是这样的:每个用户在打开一个页面之后,都会即时在数据库之内留下一个访问记录。该记录主要包括三个字段,用户 ID、IP地址、访问递增变量以及一个临时访问密码。这个临时访问密码包括了用户的认证密码以及用户访问的时间数据。每次用户登录的时候,先查 IP,如果不同则认证失败,必须重新登录;再由系统检查输入参数中的 Password,如果是从认证页登录,则清除掉该用户之前的所有访问记录;如果不是,则检查输入参数中的临时 Password,看是否与访问记录中最后一条的临时 Password 相符;如果相符,则再检查间隔时间,如果超出了 Expire 设定,则认为认证失败。
基本上可以说,这个设计是比较完美的。既满足了超时的需要,又解决了同一帐号不允许多点登录的问题。因为当一个用户从认证页登录的时候,会清掉之前的所有访问记录,也就是说即使当时还有另外一个使用相同用户的帐号在线,他也无法再继续使用,因为之前的临时密码已经无效。
然而,就是这样一个设计,却依然在测试中发现有多次认证失败的现象。并且很长一段时间内,测试组无法准确定位这个错误,也无法准确地重现这个错误。起初我们以为是临时性密码写入数据库失败,但在后来的监测中发现每次的数据库操作都正确完成,然而认证失败的错误却依然随机性地出现。迫不得已,前期和中期开发中只好暂时性地屏蔽了用户认证功能。
但是随着最后交付日期的逐渐临近,这个问题也越来越迫切。我们在测试中发现这个问题在原先的版本中就已经存在,不过也一样基本上随机性地出现。CGI 程序的调试是非常麻烦的,而这个错误往往在跟踪的时候它不出现,不跟踪的时候却出现了,实在是十分的令人郁闷。
既然跟踪不了,那就使用日志记录。终于在数以百计密密麻麻的记录中摸索出一个规律:那就是出错的地方的前一页都使用了框架页面。更进一步的分析终于成功的找出了原因所在:当使用框架页面的时候,框架页的不同部分在页面 load 的时候,都会有一个用户认证过程,而这两个用户认证过程都会生成供下一次认证使用的临时性密码。如果在两个页面的生成间隙中,恰好秒针跳过了一位,那么计算出来的密码就会有所差异。这种情况下,就自然会出现认证失败的现象。而这种情况并不是太常见,所以也就难以捕获重现了。
找到了这个原因,那就好办了。设定框架内只能有一个页面进行生成临时性密码的工作,自然就可以解决问题。当然事情也不是这么简单,再经过解决了若干个原先存在的小 Bug 之后,总算是大功告成了。
回头想一想,这个 CGI 程序对于用户认证的控制应该说是相当出色的。起初当我理解到基本的认证过程时,曾经想过,为什么要留下先后访问记录呢?只留下最后一记录岂不是更好?随后才了解到留下记录实在是个相当明智的选择,最起码的提供了对在浏览器内选择“Back/后退”的支持,因为如果不留访问记录的话,页面回退之后任何提交行为都无法认证成功。
现在基本上这个 CGI 项目算是全部结束了,真是令人长吁一口气。如果可以选择的话,希望以后再也不做 CGI 了。
一般来说,开发一个系统的过程中,排错的时间往往都要多于编码的时间。而对于维护旧有的项目,特别是由其他人开发的项目时,排错所花的时间就更要多了。
前一段时间维护的 CGI 网站,近日在调试组精细的排查下,列出了N多个问题。有些问题似乎有些匪夷所思,但却又是确实存在的,可忙坏了一帮做开发的兄弟们。我呢,今天上午刚刚听完一个答辩会,下午就接手了一个异常难啃的 BUG。
我们知道,用 ASP 或是 ASP.NET 开发的网站都可以使用一个 Session 的好东西,它可以用来保存与特定用户相关的一些信息,并可以设定一些比如超时之类的数据。但是对于 CGI 程序来说,就没有这个便利性了。而我们这个 CGI 程序,还有一些特殊的要求,比如:要求每一时刻不能同时有两个相同的用户使用系统,并且必须要实现超时限制这么一个功能。
应当说,当年这个系统的设计者还是比较出色的,基本圆满地解决了这两个问题。他的思路是这样的:每个用户在打开一个页面之后,都会即时在数据库之内留下一个访问记录。该记录主要包括三个字段,用户 ID、IP地址、访问递增变量以及一个临时访问密码。这个临时访问密码包括了用户的认证密码以及用户访问的时间数据。每次用户登录的时候,先查 IP,如果不同则认证失败,必须重新登录;再由系统检查输入参数中的 Password,如果是从认证页登录,则清除掉该用户之前的所有访问记录;如果不是,则检查输入参数中的临时 Password,看是否与访问记录中最后一条的临时 Password 相符;如果相符,则再检查间隔时间,如果超出了 Expire 设定,则认为认证失败。
基本上可以说,这个设计是比较完美的。既满足了超时的需要,又解决了同一帐号不允许多点登录的问题。因为当一个用户从认证页登录的时候,会清掉之前的所有访问记录,也就是说即使当时还有另外一个使用相同用户的帐号在线,他也无法再继续使用,因为之前的临时密码已经无效。
然而,就是这样一个设计,却依然在测试中发现有多次认证失败的现象。并且很长一段时间内,测试组无法准确定位这个错误,也无法准确地重现这个错误。起初我们以为是临时性密码写入数据库失败,但在后来的监测中发现每次的数据库操作都正确完成,然而认证失败的错误却依然随机性地出现。迫不得已,前期和中期开发中只好暂时性地屏蔽了用户认证功能。
但是随着最后交付日期的逐渐临近,这个问题也越来越迫切。我们在测试中发现这个问题在原先的版本中就已经存在,不过也一样基本上随机性地出现。CGI 程序的调试是非常麻烦的,而这个错误往往在跟踪的时候它不出现,不跟踪的时候却出现了,实在是十分的令人郁闷。
既然跟踪不了,那就使用日志记录。终于在数以百计密密麻麻的记录中摸索出一个规律:那就是出错的地方的前一页都使用了框架页面。更进一步的分析终于成功的找出了原因所在:当使用框架页面的时候,框架页的不同部分在页面 load 的时候,都会有一个用户认证过程,而这两个用户认证过程都会生成供下一次认证使用的临时性密码。如果在两个页面的生成间隙中,恰好秒针跳过了一位,那么计算出来的密码就会有所差异。这种情况下,就自然会出现认证失败的现象。而这种情况并不是太常见,所以也就难以捕获重现了。
找到了这个原因,那就好办了。设定框架内只能有一个页面进行生成临时性密码的工作,自然就可以解决问题。当然事情也不是这么简单,再经过解决了若干个原先存在的小 Bug 之后,总算是大功告成了。
回头想一想,这个 CGI 程序对于用户认证的控制应该说是相当出色的。起初当我理解到基本的认证过程时,曾经想过,为什么要留下先后访问记录呢?只留下最后一记录岂不是更好?随后才了解到留下记录实在是个相当明智的选择,最起码的提供了对在浏览器内选择“Back/后退”的支持,因为如果不留访问记录的话,页面回退之后任何提交行为都无法认证成功。
现在基本上这个 CGI 项目算是全部结束了,真是令人长吁一口气。如果可以选择的话,希望以后再也不做 CGI 了。