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

时间:2023-03-09 08:36:09
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缓存读取,所以轮询没有访问数据库),然后不访问数据库,但是数据量大的话,服务器压力也是挺大的,暂时没有更好的解决方案。