zoukankan      html  css  js  c++  java
  • MVC下的Web API限制同一账号同时登录多台设备

    原理:设备A成功登录后,会以当前会话的SessionID作为用户id作为,保存在application(保存在服务器的全局变量,多用户可以共享)变量中。设备B同账号登录后会判断application里是否已存在用户id作为值的数据,存在则将该值设为“_offline_”。设备A再在请求交互操作后,后端每次会判断SessionID作为键的值是否为“_offline_”,是则弹出该账号在异地登录,跳回登录界面。

    后端处理登录的代码(登录在Web API里写的):

    private string loginin(string content)
    {
        string returnStr = "";
        JLogin login = JsonConvert.DeserializeObject<JLogin>(content);
        JHUserBL userBL = new JHUserBL();
        JGetUser user = userBL.IsLogin(login.login_name, login.password);
        if (user != null)
        {
    
            #region 存储登录的SessionID
            HttpContext httpContext = System.Web.HttpContext.Current;
            var userOnline = (Hashtable)httpContext.Application["Online"];
            if (userOnline != null)
            {
                IDictionaryEnumerator enumerator = userOnline.GetEnumerator();
                while (enumerator.MoveNext())
                {
                    if (enumerator.Value != null && enumerator.Value.ToString().Equals(user.id.ToString()))
                    {
                        userOnline[enumerator.Key.ToString()] = "_offline_";
                        break;
                    }
                }
            }
            else
            {
                userOnline = new Hashtable();
            }
            HttpContext.Current.Session.Timeout = 60 * 24;//1天
            userOnline[HttpContext.Current.Session.SessionID] = user.id.ToString();
            httpContext.Application.Lock();
            httpContext.Application["Online"] = userOnline;
            httpContext.Application.UnLock();
            #endregion
    
            returnStr = JsonHelper.RequestSuccess(JsonConvert.SerializeObject(user));
            new JHLogBL().Insert(LogType.login, "用户登录", user.id);
        }
        else
        {
            returnStr = JsonHelper.RequstFail();
        }
        return returnStr;
    }
    

    后端处理异常登录的代码(LoginCheckFilterAttribute.cs下):

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    
    
    namespace GDSMPlateForm
    {
        public class LoginCheckFilterAttribute:ActionFilterAttribute
        {
            //表示是否检查登录
            public bool IsCheck { get; set; }
            //Action方法执行之前执行此方法
            public override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                base.OnActionExecuting(filterContext);
                if (IsCheck)
                {
                    #region 同一账号登录验证
                    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();
    
                                HttpCookie cookie = new HttpCookie("failLogin")
                                {
                                    Value = "failLogin",
                                    Expires = DateTime.Now.AddDays(1)
                                };
                                HttpContext.Current.Response.Cookies.Add(cookie);
                                HttpContext.Current.Request.Cookies.Remove("user_id");
                                //string msg = "下线通知:当前账号另一地点登录, 您被迫下线。若非本人操作,您的登录密码很可能已经泄露,请及时改密。";
                                filterContext.HttpContext.Response.Redirect("/");
                            }
                        }
                    }
                    #endregion
    
    
                    string req_method = filterContext.HttpContext.Request.HttpMethod;
    
                    if (req_method.ToLower() != "get" && req_method.ToLower() != "post" && req_method.ToLower() != "head")
                    {
                        GDSMCommon.LogHelper.WriteLog("平台请求方式有错:" + req_method);
                        filterContext.HttpContext.Response.Redirect("/error.html");
                    }
                    if (HttpContext.Current.Request.UrlReferrer != null)
                    {
                        if (HttpContext.Current.Request.Url.Host != HttpContext.Current.Request.UrlReferrer.Host)
                        {
                            GDSMCommon.LogHelper.WriteLog("跨站请求伪造:当前Host:" + HttpContext.Current.Request.Url.Host + ",上一个Host:" + HttpContext.Current.Request.UrlReferrer.Host);
                            filterContext.HttpContext.Response.Redirect("~/error.html");
                        }
                    }
                    string full_url = HttpContext.Current.Request.Url.ToString().ToLower();
                    if (full_url.Contains("content-type") || full_url.Contains("content-transfer-encoding") || full_url.Contains(";") || full_url.Contains("=_"))
                    {
                        GDSMCommon.LogHelper.WriteLog("参数请求错误:" + HttpContext.Current.Request.Url.ToString());
                        filterContext.HttpContext.Response.Redirect("~/error.html");
                    }
                    //else
                    //{
                    //    GD.Common.LogHelper.WriteLog("跨站请求伪造:当前Host:" + HttpContext.Current.Request.Url.Host + ",上一个Host:不存在" );
                    //    filterContext.HttpContext.Response.Redirect("~/error.html");
                    //}
                    string url = HttpContext.Current.Request.Url.AbsolutePath.ToString().ToLower();
                    if (url != "/" && url != "/home/index" && url != "/home/" && url != "/home" && url.Contains("afileupload") == false && url.Contains("classiccase") == false && url.Contains("h5template") == false && url.Contains("registeragree") == false && url.Contains("bannerupload") == false)
                    {
                        校验用户是否已经登录
                        //if (filterContext.HttpContext.Session["UserInfo"] == null)
                        //{
                        //    //跳转到登陆页
                        //    filterContext.HttpContext.Response.Redirect("/?tourl=" + HttpContext.Current.Server.UrlEncode(HttpContext.Current.Request.Url.ToString()));
                        //}
    
                        if (HttpContext.Current.Request.Cookies["user_id"] == null)
                        {
    
                            filterContext.HttpContext.Response.Redirect("/?tourl=" + HttpContext.Current.Server.UrlEncode(HttpContext.Current.Request.Url.ToString()));
    
                        }
                        else
                        {
                            if (HttpContext.Current.Request.Cookies["user_id"].Value == "")
                            {
                                HttpCookie cookie = HttpContext.Current.Request.Cookies["user_id"];
                                cookie.Expires = DateTime.Now.AddDays(-1);
                                HttpContext.Current.Request.Cookies.Add(cookie);
                                filterContext.HttpContext.Response.Redirect("/?tourl=" + HttpContext.Current.Server.UrlEncode(HttpContext.Current.Request.Url.ToString()));
                            }
                        }
                    }
    
                }
            }
        }
    }
    
    

    然后在首页处理重复登录的状态:

    $(function () {
        var failLogin = getCookie("failLogin");
        var user_id = getCookie("user_id");
        if (failLogin != "" && user_id != "") {
            var msg = "下线通知:当前账号另一地点登录, 您被迫下线。若非本人操作,您的登录密码很可能已经泄露,请及时改密。";
            layer.open({
                content: msg,
                end: function () {
                    document.cookie = "failLogin=";
                    delCookie("user_id");
                }
            });
        }
    });
    

    在MVC的WebApi中默认是没有开启Session会话支持的。需要在Global中重写Init方法来指定会话需要支持的类型

    public class WebApiApplication : System.Web.HttpApplication
    {
        public override void Init()
        {
            this.PostAuthenticateRequest += (sender, e) => HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
            base.Init();
        }
    }

    在WebApiConfig中建立建立HttpControllerHandler和HttpControllerRouteHandler 并覆写它,同时Routes.MapHttpRoute要修改为下面的格式

    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API 配置和服务
    
            // Web API 路由
            //config.MapHttpAttributeRoutes();
    
            //config.Routes.MapHttpRoute(
            //    name: "DefaultApi",
            //    routeTemplate: "api/{controller}/{id}",
            //    defaults: new { id = RouteParameter.Optional }
            //);
    
            RouteTable.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            ).RouteHandler = new SessionControllerRouteHandler();
        }
    
        public class SessionRouteHandler : HttpControllerHandler, IRequiresSessionState
        {
            public SessionRouteHandler(RouteData routeData)
                : base(routeData)
            {
            }
        }
        public class SessionControllerRouteHandler : HttpControllerRouteHandler
        {
            protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
            {
                return new SessionRouteHandler(requestContext.RouteData);
            }
        }
    }
    

    SessionID每次请求一直会变的情况,Global页面下需要添加Session_Start方法

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Http;
    using System.Web.Mvc;
    using System.Web.Optimization;
    using System.Web.Routing;
    using System.Web.SessionState;
    
    namespace GDSMPlateForm
    {
        public class MvcApplication : System.Web.HttpApplication
        {
            protected void Application_Start()
            {
                AreaRegistration.RegisterAllAreas();
                GlobalConfiguration.Configure(WebApiConfig.Register);
                FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
                RouteConfig.RegisterRoutes(RouteTable.Routes);
                BundleConfig.RegisterBundles(BundleTable.Bundles);
            }
    
            void Session_Start(object sender, EventArgs e)
            {
                // Code that runs when a new session is started
    
            }
    
            void Session_End(object sender, EventArgs e)
            {
                // Code that runs when a session ends. 
                // Note: The Session_End event is raised only when the sessionstate mode
                // is set to InProc in the Web.config file. If session mode is set to StateServer 
                // or SQLServer, the event is not raised.
            }
        }
    
        public class WebApiApplication : System.Web.HttpApplication
        {
            public override void Init()
            {
                this.PostAuthenticateRequest += (sender, e) => HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
                base.Init();
            }
    
            void Session_Start(object sender, EventArgs e)
            {
                // Code that runs when a new session is started
    
            }
    
            void Session_End(object sender, EventArgs e)
            {
                // Code that runs when a session ends. 
                // Note: The Session_End event is raised only when the sessionstate mode
                // is set to InProc in the Web.config file. If session mode is set to StateServer 
                // or SQLServer, the event is not raised.
            }
        }
    
    
    }
    
  • 相关阅读:
    9 种数据库中Select Top的使用方法 (只显示数据库的前几条记录)(Oracle、Infomix、DB2、SQL Server、Access、Sybase、MySQL、FoxPro、Sqlite)
    Delphi Treeview用法介绍
    TTreeView、TTreeNodes和TTreeNode
    delphi中TreeView使用
    Delphi 中多线程同步的一些处理方法
    delphi 中几种多线程操作方式
    Delphi 多线程介绍,以及线程类TThread 分析
    Delphi TTcpClient 和 TTcpServer 类 (TBaseSocket、TIpSocket、TCustomIPClient、TTcpClient、TUdpSocket、TRawSocket、TCustomTcpServer、TTcpServer) 介绍
    用NVIDIA Tensor Cores和TensorFlow 2加速医学图像分割
    通过Mellanox ConnectX NIC使用XDP加速
  • 原文地址:https://www.cnblogs.com/zt102545/p/13940246.html
Copyright © 2011-2022 走看看