天下無雙
阿龍 --质量是流程决定的。

防止一个用户名同时登录的另类方法

最近,公司有个BS结构的项目的deadline临近,但是在处理“单人登入,即时登出”时,一直没有找到一个比较理想的方案,因为使用这个项目的客户群是警察叔叔,如果在登入登出的时候“卡壳”,那我不要混了,公司也得玩完。
防止同一帐号多人同时登入的一般解决方案,无非就是把登入时的用户ID保存在数据库中,或application中,或cache中,然后在用户登出的时候删除用户ID,但是要删除用户ID还是有点棘手。
不管用户ID保存在哪里,当我点击“登出”button的时候,都需要我们通过一个事件去告诉系统,用户ID现在该清除了。假设我把用户ID保存到cache中,并设置cache的销毁时间为Session的结束时间,Session一结束,cache跟着销毁,这样设计也合情合理。
如果您一相情愿的认为使用者都会去点击“登出”button,那就大错特错了,我平常也喜欢直接X掉IE,懒得去点那小小的“登出”button。可能有人马上会说,可以用js来判断浏览器是否关闭了呀!没错,js很容易判断IE是否关闭。但是,如果电源插头不小心被你那急着上WC的,有点肾亏同事绊掉了呢?那些警察叔叔就只能干等着session自动过期,如果Timeout设置得稍微长一点,客户那边马上电话不断,然后,公司技术员不得不restart服务器。公司以前有些系统就如此,老要restart服务器,我是看在眼上,痛在心里。如果还有人要说,俺有UPS,怕什么,那我也不好再说什么了^-^
我想,如果单靠JS来判断,“年长日久,难免发生意外”,怎么办呢?能不能让服务器自己来判断呢?这还问,当然可以,把session的Timeout的值设置小点,不就得了!如果只个值太小,等警察叔叔抽完一根烟,再使用系统的时候,系统马上提示;“您的会话已经过期,请重新登录“。如果老是要登入,那就玩不下去了。能不能想个办法,不让系统会话很快过期,又能在一个合适的时间让系统自动注销呢?可能没等我说完,马上有人会说,在client端适时的激活服务器,不就得了。您说的没错,我也是这么想的。
那我就按您的思路去实现吧;
首先设置web.config文件;


<sessionState mode="StateServer" cookieless="false" stateConnectionString="tcpip=127.0.0.1:42424" sqlConnectionString="data source=127.0.0.1;Trusted_Connection=yes" timeout="1"/>
      然后在登入事件中加入;
      ///同一时间点,检查登录用户ID的唯一性
///</summary>
    ///<param name="strZh">用户ID </param>
    ///<returns></returns>
    private bool ckeckUserOnlyOne(string strZh)
    {
        string strTempZh=strZh;
        if (Cache[strZh] == null)
        {
            TimeSpan SessTimeout = new TimeSpan(0,0,System.Web.HttpContext.Current.Session.Timeout,0,0);
            HttpContext.Current.Cache.Insert(strTempZh,strTempZh,null, DateTime.MaxValue, SessTimeout, System.Web.Caching.CacheItemPriority.NotRemovable, null);
            return true;
        }
        else
            return false;
}


然后在登出事件加入;


  try
        {
            Session.Abandon();
            System.Web.HttpContext.Current.Session.RemoveAll();
            Cache.Remove("用户ID");
        }
        catch (Exception ex)
        { }


主角登场了,在每个页面引入下面的JS;


<script>
var step=0;
    function myRefresh()
    {
        var httpRequest = new ActiveXObject("microsoft.xmlhttp");
        httpRequest.open("GET", "delSession.aspx", false);
        httpRequest.send(null);
        step ++;
        if(step <2)//注意2
        {
            setTimeout("myRefresh()",30*1000); //30秒
        }
    }
    myRefresh();
</script>
   


最后,别忘记项目的Root目录下new一个delSession.aspx文件,然后在她的page_laod方法中写上Response.Expires = -1;
    按ctr+F5,期间不要激活,是不是在2分种之后,您的系统提示会话过期了呢?哈哈
由于代码比较简单,就不一一解释了,讲一下思路;
首先在未继续激活会话的情况下,设置她的生命周期为一分钟,我感觉一分钟刚好,(哎,自古红颜多薄命!)
然后,在判断此时userID 没有登入时,把user ID 保存在cache中,且cache的过期时间为SessTimeout,也就是说当前会话一销毁,cache也就跟着殉情。(烈女啊!)
    又然后,在“登出“事件中注销当前会话,清除cache集合中的当前user ID,这样就可以在点击“登出”button时,及时清除对应的cache项。
    再然后,在页面的JS中通过setTimeout("myRefresh()",30*1000)方法,不断请求激活服务器,直至玩不下去,注意并没有获取响应。
    再然后,在被请求的页面中设置自己不使用“页面输出缓存”,以免无法激活当前会话。
现在已经是0:16分了,长沙的冬天晚上比较寒冷,刚刚泡的大麦红茶已经凉了,再罗嗦一句,可以修改“step <2”和setTimeout("myRefresh()",30*1000),把这些参数放到配置文件中,可以随便修改,直到找到最佳平衡点,祝大家好运!
后记;
    言多必失,说得不妥当之处,还望多多指教!

posted on 2008-04-04 11:42  阿龍  阅读(347)  评论(2编辑  收藏  举报