asp.net mvc 简单实现一个账号只能在一个地方登录

原理:

    假设用户在机器A登陆后,

    这时用户再次在机器B登陆,会以当前会话的SessionID作为,用户id作为,插入dictionary集合中,集合再保存在application(保存在服务器的全局变量,多用户可以共享)变量中,

         同时判断集合中是否有其他值,这里A机器已经登陆,所以会有A机器登陆的键值对,将A机器的键对应值修改为“_offline_”,以表示强制下线,

    A机器的页面通过js轮询去查询dictionary集合,发现中SessionID键对应的值被修改为“_offline_”,从而注销登陆,并提示被迫下线。

1、global中的代码:

public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();

            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }

        //保证同一次会话的SessionID不变
        protected void Session_Start(object sender, EventArgs e) 
        { }

        protected void Session_End(object sender, EventArgs e)
        {
            Hashtable hOnline = (Hashtable)Application["Online"];
            if (hOnline != null)
            {
                if (hOnline[Session.SessionID] != null)
                {
                    hOnline.Remove(Session.SessionID);
                    Application.Lock();
                    Application["Online"] = hOnline;
                    Application.UnLock();
                }
            }
        }
    }
注:保证同一次会话的SessionID不变,这点很重要

2、用户登陆代码:
.....

HttpContext httpContext = System.Web.HttpContext.Current;
            var userOnline = 
(Dictionary<string,string>)httpContext.Application["Online"];
            if (userOnline != null)
            {

            IDictionaryEnumerator enumerator = userOnline.GetEnumerator();
            while (enumerator.MoveNext())
            {
              if (enumerator.Value != null && enumerator.Value.ToString().Equals(userID.ToString()))
              {
                userOnline[enumerator.Key.ToString()] = "_offline_";
                break;
              }
            }

            }

else
            {
                userOnline = new Hashtable();
            }
            userOnline[Session.SessionID] = userID.ToString();
            httpContext.Application.Lock();
            httpContext.Application["Online"] = userOnline;
            httpContext.Application.UnLock();

......

4、页面轮询(可以在母版页,公共页)

前台js用的easyui

$(document).ready(function () {
        //定时检测是否被强制下线
            setInterval(function () {
                CheckIsForcedLogout();
            }, 5000);
    });

    //检测是否被强制下线
    function CheckIsForcedLogout() {
        $.ajax({
            url: "/Home/CheckIsForcedLogout",
            type: "POST",
            dataType: "json",
            success: function (msg) {
                if (msg.OperateResult == "Success") {
                    $.messager.alert('', msg.OperateData, 'error', function () {
                        window.location.href = "/Account/Login";
                    });
                }
            },
            error: function (ex) { }
        });
    }
 [HttpPost]
        public JsonResult CheckIsForcedLogout()
        {
            try
            {
                HttpContext httpContext = System.Web.HttpContext.Current;
                Hashtable userOnline = (Hashtable)httpContext.Application["Online"];if (userOnline != null)
                {
                    if (userOnline.ContainsKey(httpContext.Session.SessionID))
                    {
                        var value=userOnline[httpContext.Session.SessionID];
                        //判断当前session保存的值是否为被注销值
                        if (value != null && "_offline_".Equals(value))
                        {
                            //验证被注销则清空session
                            userOnline.Remove(httpContext.Session.SessionID);
                            httpContext.Application.Lock();
                            httpContext.Application["online"] = userOnline;
                            httpContext.Application.UnLock();

                            string msg = "下线通知:当前账号另一地点登录, 您被迫下线。若非本人操作,您的登录密码很可能已经泄露,请及时改密。";

                            //登出,清除cookie
                            FormsAuthentication.SignOut();

                            return Json(new { OperateResult = "Success", OperateData = msg }, JsonRequestBehavior.AllowGet);
                        }
                    }
                }
                return Json(new { OperateResult ="Failed" }, JsonRequestBehavior.AllowGet);
            }
            catch (Exception ex)
            {
                return Json(new { OperateResult = "Failed" }, JsonRequestBehavior.AllowGet);
            }
        }

这里登陆后,每5秒轮询服务器(获取最后登陆时间、ip是从redis缓存读取,所以轮询没有访问数据库),然后不访问数据库,但是数据量大的话,服务器压力也是挺大的,暂时没有更好的解决方案。

原文地址:https://www.cnblogs.com/qk2014/p/7988383.html