zoukankan      html  css  js  c++  java
  • ASP.net 实现禁止用户重复登录

    本文先为大家介绍如何利用缓存Cache方便地实现此功能。 
    Cache与Session这二个状态对像的其中有一个不同之处,Cache是一个全局对象,作用的范围是整个应用程序,所有用户;
    而Session是一个用户会话对象,是局部对象,用于保存单个用户的信息。 
    只要把每次用户登录后的用户信息存储在Cache中,把Cache的Key名设为用户的登录名,Cache的过期时间设置为Session的超时时间,在用户每次登录的时候去判断一下Cache[用户名]是否有值,如果没有值,证明该用户没有登录,否则该用户已登录。
    为大家举一个例子吧。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    /// <summary>
    /// 防止多次登录
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Button1_Click(object sender, System.EventArgs e)
    {
    string strUser = string.Empty;
    string strCacheKey = this.TextBox1.Text;
     
    strUser = Convert.ToString(Cache[strCacheKey]);
     
    if (strUser == string.Empty)
    {
    TimeSpan SessTimeOut = new TimeSpan(0, 0, System.Web.HttpContext.Current.Session.Timeout, 0, 0);
     
    Cache.Insert(strCacheKey, strCacheKey, null, DateTime.MaxValue, SessTimeOut, CacheItemPriority.NotRemovable, null);
    Session["User"] = strCacheKey;
    this.Label1.Text = Session["User"].ToString();
    }
    else
    {
    this.Label1.Text = "这个用户已经登录!";
    }
    }

    在网上又找了下,发现了另外两种解决方案:
    1、通过数据库状态位判断该用户是否已经登录。
    2、利用session监听器监听每一个登录用户的登录情况。
    第一种解决方案很简单,但需要考虑用户非正常退出的情况,如直接关闭浏览器等等,可用性较低。
    接下来,主要介绍第二种方案的具体实现:利用session监听器监听每一个登录用户的登录情况。
    A.用户登录后,先去数据库查询该登录名是否存在、是否锁定,在登录名存在且非锁定的情况下,从application内置作用域对象中取出所有的登录信息,查看该登录名是否已经登录,如果登录了,就友好提示下;反之表示可以登录,将该登录信息保存在application中。
    主要代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    //
    //所有的登录信息
    Map<String, String> loginUserMap = (Map<String, String>) super.getApplicationAttr(Constant.LOGIN_USER_MAP);
    boolean isExist = false;
    String sessionId = super.getSessionId(false);
    if(loginUserMap==null){
    loginUserMap = new HashMap<String, String>();
    }
    for (String username : loginUserMap.keySet()) {
    //判断是否已经保存该登录用户的信息,是否为同一个用户进行重复登录
    if(!username.equals(user.getFuUserName()) || loginUserMap.containsValue(sessionId)){
    continue;
    }
    isExist = true;
    break;
    }
    if(isExist){
    //该用户已登录
    //
    }else {
    //该用户没有登录
    loginUserMap.put(result.getFuUserName(), sessionId);
    //
    }
    //

    B.登录考虑完之后,来考虑考虑退出。
    用户正常退出时,我们需要将该用户的登录信息从session中移除。我们可以写一个Session监听器,监听sessioon销毁的时候,我们将登录的用户注销掉,也就是从application中移除。表示该用户已经下线了。

    主要代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    //
    public void sessionDestroyed(HttpSessionEvent event) {
      // 
      //在session销毁的时候 把loginUserMap中保存的键值对清除
      User user = (User)event.getSession().getAttribute("loginUser");
      if(user!=null){
        Map<String, String> loginUserMap = (Map<String, String>)event.getSession().getServletContext().getAttribute("loginUserMap");
        loginUserMap.remove(user.getFuUserName());
    event.getSession().getServletContext().setAttribute("loginUserMap",loginUserMap);
      }
      //
    }
    //

    另外,还有一个问题,如果说登录的用户突然关闭了浏览器而没有点击退出按钮。那么可以利用beforeunload 事件,在浏览器刷新或者关闭的时候触发。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //在刷新或关闭时调用的事件
    $(window).bind('beforeunload',function(){
     $.ajax({
      url:"${ctx}/system/user/user!logout.action",
      type:"post",
      success:function(){
       alert("您已退出登录");
      }
     });
    });

    这样基本就实现了需求。
    大家可以把上面代码运用到自己的项目中,检测一下,有效的防止同一账号的重复登录,希望大家喜欢这些方法。

    ASP.net 实现禁止用户重复登录(踢出先登录的)

    应客户要求,须在系统中实现禁止用户重复登录。

    我采用的是asp.net自带的登录组件,有很多现成属性和方法可用,比如,Membershipuser.Comment可用来存储用户的特定登录信息,本例中的时间刻录,就存储在这个属性中。注意,存储后,记得用Membership.UpdateUser 方法来保存信息。

    如果没有采用asp.net自带的登录组件,则可通过Application或数据库来保存,其原理是一样的。

    为了解决用户提出的“禁止同一用户名重复登录”的要求,曾经采用更两种解决方案。

    最初采用的方案,是利用MembershipUser.IsOnline 来判断用户是否在线,如果在线,则禁止登录。

    If sUser.IsOnline Then

    Me.CustomValidator1.ErrorMessage = "该用户已经在另一地点登录!"
               args.IsValid = False
               Exit Sub
        End If

     

     

    但是,这一方案有一缺点,当用户非正常退出时,由于Session值的消失要20分钟左右,因此在这20分钟内,系统无法判断客户端是否已经退出,只能作为用户仍然在线来对待。也就是说,这20分钟内,该用户无法再次登录,必须等20分钟后,方可正常登录。

    这样明显无法满足客户要求。所以,后来我就采用了第二种方案:后登录的用户,踢出先登录的用户。

    第二种方案的实现原理如下:

    1.登录时在User.Comment中保存登录的时间刻度,同时在Session中保存同一刻度数。代码如下:
            If Membership.ValidateUser(sUserName, sPassword) Then

     

    Dim sLoginTicks As String = Now.Ticks.ToString
                sUser.Comment = sLoginTicks
                Membership.UpdateUser(sUser)  '保存到User.comment值中
                Page.Session.Add("LoginTicks", sLoginTicks) '保存到Session中

     

    FormsAuthentication.SetAuthCookie(sUserName, False)
                Response.Redirect("Default.aspx")
            Else
                Response.Write("<script> alert(""用户名或密码错误!"");location.href = """ & Request.Url.ToString & """;</script>")
                Response.End()
            End If

     

    2.在页面登录验证时,对比Session中时间刻度,和User.comment中的时间刻度是否相同。后登录的,这两个值同时被更新,必定会相同。而先登录的用户,由于Session中的值未更新,而User.comment中的值,被后登录的用户更新掉了,因此值不同,从而被踢出。代码如下:

    Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
           Dim HomePage As String = Me.ResolveUrl("~/Default.aspx")
            If Page.Request.IsAuthenticated = True Then '本句用来验证是否登录
                Dim sUser As MembershipUser = Membership.GetUser
                If Page.Session("LoginTicks") IsNot Nothing Then
                    If Me.Page.Session("LoginTicks").ToString <> sUser.Comment.ToString Then
                        FormsAuthentication.SignOut()
                        Response.Write("<script> alert(""相同帐号在其它地点登录,你被迫注销。如有疑问,请与网站管理员联系。"");location.href = """ & HomePage & """;</script>")
                        Response.End()
                    End If
                End If
            End If

     

    End Sub

    经测试,该方案完美解决了禁止用户重复登录的问题

     

    二、

    实现思路:
    用户登录成功后,将用户登录信息存放到Hashtable类型的Application["Online"]里面,其键值为SessionID,其Value值为用户ID;当用户注销时,调用Session.Abandon;在Global.asax里面的SessionEnd事件中,将用户ID从Hashtable中删除;在用户访问页面时,察看Hashtable中是否有对应的用户ID如果没有则判断用户不在线(用户不在线的原因可能是按了注销按钮、网页超时等)

    1、公用类中判断用户是否在线的函数(供用户调用)
    Code
     /**//// <summary> 
     /// 判断用户strUserID是否包含在Hashtable h中 
     /// </summary> 
     /// <param name="strUserID"></param> 
     /// <param name="h"></param> 
     /// <returns></returns> 
     public static bool AmIOnline(string strUserID, Hashtable h)
     {
         if (strUserID == null)
            return false;

        //继续判断是否该用户已经登陆 
        if (h == null)
            return false;

        //判断哈希表中是否有该用户 
        IDictionaryEnumerator e1 = h.GetEnumerator();
        bool flag = false;
        while (e1.MoveNext())
        {
            if (e1.Value.ToString().CompareTo(strUserID) == 0)
            {
                flag = true;
                break;
            }
        }
        return flag;
    }

    2、用户登录事件处理:
    Code
    private void btnlogin_Click(object sender, System.Web.UI.ImageClickEventArgs e)

        //User为自定义的类,其中包含Login方法
        User CurUser = new User();
        CurUser.UserID = this.username.Text.Trim();

        if (MyUtility.AmIOnline(CurUser.UserID, (Hashtable) Application["Online"]))
        {
            JScript.Alert("您所使用的登录ID已经在线了!您不能重复登录!");
            return;
        }

        CurUser.LoginPsw = FormsAuthentication.HashPasswordForStoringInConfigFile(this.password.Text.Trim(), "SHA1");
        int ii = CurUser.Login();
        StringBuilder sbPmt = new StringBuilder();

        switch (ii)
        {
        case 0: //如果登录成功,则将UserID加入Application["Online"]中
            Hashtable h = (Hashtable) Application["Online"];
            if (h == null)
                h = new Hashtable();
            h[Session.SessionID] = CurUser.UserID;
            Application["Online"] = h;

            Session["UserID"] = CurUser.UserID;
            Session["UserNM"] = CurUser.UserNM;
            Session["RoleMap"] = CurUser.RoleMap;
            Session["LoginPsw"] = CurUser.LoginPsw;
            Session["LoginTime"] = DateTime.Now;
            Response.Redirect("ChooseRole.aspx");
            break;
        case -1:
            JScript.Alert("用户名错误!");
            break;
        case -2:
            JScript.Alert("密码错误!");
            break;
        default:
            sbPmt.Append("登录过程中发生未知错误!");
            JScript.Alert(sbPmt.ToString());
            break;
        }
        return;
    }

    3、在Global.asax中的Session_End事件:
    Code
    protected void Session_End(Object sender, EventArgs e)
    {
        Hashtable h = (Hashtable) Application["Online"];

        if (h[Session.SessionID] != null)
            h.Remove(Session.SessionID);

        Application["Online"] = h;
    }

    4、在每一个页面需要刷新的地方,调用如下代码:
    Code
    try
    {
        if (!common.MyUtility.AmIOnline(Session["UserID"].ToString(), (Hashtable) Application["OnLine"]))
        {
            //用户没有在线 ,转到登录界面
            Response.Write("<script>parent.document.location.href='Login.aspx';</script>"); ////有框架时用
            //Response.Redirect("login.aspx"); ////无框架时用
            return;
        }
    }
    catch
    {
        //会话过期 ,转到登录界面
        Response.Write("<script>parent.document.location.href='Login.aspx';</script>"); ////有框架时所用
        //Response.Redirect("login.aspx"); ////无框架时用
        return;
    }

    深入思考:

    由本例的解决方法可以加以延伸,比如,在存储UserID的时候,将UserID+客户端IP地址一起存进去,

    则在将相应信息取出来分析的时候,可以做到:当用户在不同的计算机上先后登录的时候,则允许最近一次的登录,而将之前的登录删除!等等

  • 相关阅读:
    thinkphp5 tp5 命名空间 报错 Namespace declaration statement has to be the very first statement in the script
    开启 php 错误 提示 php-fpm 重启 nginx 500错误 解决办法 wdlinux lnmp 一键包 php脚本无法解析执行
    js 设置 cookie 定时 弹出层 提示层 下次访问 不再显示 弹窗 getCookie setCookie setTimeout
    php 二维数组 转字符串 implode 方便 mysql in 查询
    nginx 重启 ps -ef|grep nginx kill -HUP 主进程号
    jquery bootstrap help-block input 表单 提示 帮助 信息
    jquery 倒计时 60秒 短信 验证码 js ajax 获取
    jQuery如何获取同一个类标签的所有的值 遍历
    linux下C语言文件操作相关函数
    gcc,gdb用法
  • 原文地址:https://www.cnblogs.com/xdot/p/5435496.html
Copyright © 2011-2022 走看看