【问题标题】:Reusable way to allow an account to be used by a single person at a time允许一个人一次使用一个帐户的可重用方式
【发布时间】:2013-08-08 22:44:00
【问题描述】:

我做了一个功能,可以防止一个用户名同时登录多个,我在 Actions 中这样称呼它:

        int userId = (int)WebSecurity.CurrentUserId;

        if ((this.Session.SessionID != dba.getSessionId(userId)) || dba.getSessionId(userId) == null)
        {
            WebSecurity.Logout();
            return RedirectToAction("Index", "Home");
        }

所以重点是每次用户登录时,我都会将他的 sessionID 保存到数据库字段中。因此,如果具有相同用户名的人登录已经使用相同用户名登录的人,它将用这个新会话覆盖该数据库字段。如果数据库中的 sessionID 与登录用户的当前会话 ID 不同,则将其注销。

是否有可能将这部分代码放在一个地方,或者我必须将它放在我的应用程序的每个操作中?

我在 Global.asax 中试过:

void Application_BeginRequest(object sender, EventArgs e)
    {
        if (Session["ID"] != null)
        {
            int userId = Convert.ToInt32(Session["ID"]);
            if ((this.Session.SessionID != db.getSessionId(userId)) || db.getSessionId(userId) == null)
            {
                WebSecurity.Logout();
            }
        }
    }

但是如果我这样尝试,我不能在这里使用 Session 或 WebSecurity 类:

    void Application_BeginRequest(object sender, EventArgs e)
    {
        int userId = (int)WebSecurity.CurrentUserId;

        if ((this.Session.SessionID != db.getSessionId(userId)) || db.getSessionId(userId) == null)
        {
            WebSecurity.Logout();
            Response.RedirectToRoute("Default");                
        }
    }

因为我得到空引用异常。

编辑

我用过这个:

    void IActionFilter.OnActionExecuting(ActionExecutingContext filterContext)
    {
        int userId = (int)WebSecurity.CurrentUserId;
        using (var db = new UsersContext())
        {
            string s = db.getSessionId(userId);

            if ((filterContext.HttpContext.Session.SessionID != db.getSessionId(userId)) || db.getSessionId(userId) == null)
            {
                WebSecurity.Logout();
                filterContext.Result = new RedirectResult("/Home/Index");
            }
        }
    }

我必须使用 using 语句作为上下文,否则 db.getSessionId(userId) 会返回旧的 sessionId。方法是这样的:

    public string getSessionId(int userId)
    {
        string s = "";
        var get = this.UserProfiles.Single(x => x.UserId == userId);
        s = get.SessionId;
        return s;
    }

非常奇怪,必须了解为什么会发生这种情况。

一切正常,除了一件事。我在控制器中有一个 JsonResult 操作,它返回 Json,但由于事件(其输入事件上的文本框)无法触发 POST(我认为这是因为它之前注销)重定向不起作用。它甚至无法发布到该 Json 操作以接收回调和重定向。有什么线索吗?

                        success: function (data) {
                        if (data.messageSaved) {
                            //data received - OK!
                        }
                        else {
                            // in case data was not received, something went wrong redirect out
                            window.location.href = urlhome;
                        }
                    }

在我使用 ActionFilterAttribute 之前,我使用代码检查 POST 中的不同会话,当然它可以进行回调,因此如果没有收到数据则重定向。但现在因为它甚至不能 POST 并进入方法它只是卡在那里并且不重定向:)

【问题讨论】:

  • +1 实际上担心代码重用 :)
  • 这不是同一个问题,但 Jimmy Bogard 的演讲“让你的脂肪控制者节食”总是值得一看,即使只是因为它对 MVC 世界的深入了解。跨度>

标签: asp.net-mvc


【解决方案1】:

我会派生自AuthorizeAttribute。如果您不需要授权请求,则无需检查此信息。

public class SingleLoginAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    { 
        bool isAuthorized = base.AuthorizeCore(httpContext);

        if (isAuthorized)
        {
            int userId = (int)WebSecurity.CurrentUserId;

            if ((filterContext.HttpContext.Session.SessionID != dba.getSessionId(userId)) 
                || dba.getSessionId(userId) == null)
            {
                WebSecurity.Logout();
                isAuthorized = false;
                filterContext.Result = new RedirectResult("/Home/Index");
            }
        }

        return isAuthorized;
    }

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            filterContext.Result = new JsonResult()
            {
                Data = FormsAuthentication.LoginUrl,
                JsonRequestBehavior = JsonRequestBehavior.AllowGet
            };
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}

我还要提到,这允许您短路其他 ActionFilter,因为它们在 OnAuthorization 之后运行。

  1. 转发订单 - OnAuthorization : AuthorizationFilter(范围控制器)
  2. 转发订单 - OnActionExecuting:ActionFilter1(全球范围)
  3. 转发订单 - OnActionExecuting:ActionFilter2(范围控制器)
  4. 转发订单 - OnActionExecuting:ActionFilter3(作用域操作)

然后正如Rob Lyndon 提到的,您可以在 FilterConfig (MVC4) 中

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)
    {
        filters.Add(new SingleLoginAuthorizeAttribute());
    }
}

然后,当您不想要求任何授权时,可以在 ActionResult 方法或控制器类上使用AllowAnonymouseAttribute 来允许匿名访问。

更新

我为您的 ajax 调用(Get 或 Post)添加了一种处理超时的方法。您可以执行以下操作:

success: function (jsonResult)
{
  if (jsonResult.indexOf('http') == 0)
  {
    window.location = jsonResult;
  }

  // do other stuff with the Ajax Result
}

这并不是最好的方法,但如果您想了解更多关于如何更好地做到这一点的信息,我会问另一个问题,而不是在这个问题上附加更多问题。

【讨论】:

  • +1 表示派生自 AuthorizeAttribute 的想法,但有一点小问题是,您正在使用 AllowAnonymousAttribute 覆盖其跳过授权的能力。
  • 我自己一直在查看源代码。您可以做的是覆盖AuthorizeCore 方法;那么你就不需要base.OnAuthorization() 调用,AllowAnonymous 会让你选择退出整个安全检查,这正是我所期望的。
  • 看起来不错。然后,如果您将其添加为全局过滤器,您可以从选择加入安全模型转移到选择退出安全模型。
  • 我总是选择加入退出 :)
  • 正如 Erik 所说,这可能不是它的论坛,但您实际上可以添加一个过滤器提供程序来区分 Ajax 和非 Ajax 请求,并相应地调整属性。如果您提出另一个问题,我们可以带您完成该过程。这样做的另一个好处是可以很容易地使用过滤器提供程序连接到您的数据库——这将是对 MVC 的深入了解。
【解决方案2】:

ActionFilterAttribute 是要走的路。

【讨论】:

    【解决方案3】:

    我们创建了一个名为SeatCheckAction Filter 并像这样装饰每个控制器:

    [SeatCheck]
    public class NoteController : BaseController
    {
    

    我们使用它来计算座位数和其他功能,但它可以更轻松地控制任何地方而无需考虑它。

    在 proejct ActionFilters 文件夹中,我们有一个如下所示的 SeatCheck.cs 文件:

    namespace site.ActionFilters
    {
      public class SeatCheckAttribute : ActionFilterAttribute
      {
         public override void OnActionExecuting(ActionExecutingContext filterContext)
         {
    

    你可以像这样在 Action Filter 中获取 SessionID

     filterContext.HttpContext.Session.SessionID
    

    【讨论】:

      【解决方案4】:

      创建一个custom action filter,并将该代码放入过滤器中,然后将过滤器应用于您的控制器。

      【讨论】:

        【解决方案5】:

        是的,确实有。您可以使用从ActionFilterAttribute 派生的属性。

        我会写一个名为SessionSecurityAttribute的类:

        public class SessionSecurityAttribute : ActionFilterAttribute
        {
            public MyDbConn MyDbConn { get; set; }
        
            public override void OnActionExecuting(ActionExecutingContext filterContext)
            {
                var session = filterContext.RequestContext.HttpContext.Session;
                if (session["ID"] != null && WebSecurity.IsAuthenticated)
                {
                    int userId = Convert.ToInt32(session["ID"]);
                    if ((sessionID != MyDbConn.getSessionId(userId)) || MyDbConn.getSessionId(userId) == null)
                    {
                        WebSecurity.Logout();
                    }
                }
            }
        }
        

        问题仍然存在:如何将这些属性添加到您的操作中,同时让它们访问您的数据库?这很简单:在Global.asax 中,您可以调用引导RegisterGlobalFilters 方法:

        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute());
            filters.Add(new SessionSecurityAttribute
                {
                    MyDbConn = DependencyResolver.Current.GetService<MyDbConn>()
                });
        }
        

        默认情况下,这会将您的 SessionSecurityAttribute 与数据库连接添加到每个操作中,而无需一行重复的代码。

        【讨论】:

        • 感谢您的帮助。我只想补充一点,Session["ID"] 在这里不起作用。您可能只是忘记添加filterContext.RequestContext.HttpContext.Session["ID"]
        【解决方案6】:

        您可以尝试实现自己的自定义 ISessionIDManager: http://msdn.microsoft.com/en-us/library/system.web.sessionstate.isessionidmanager.aspx

        在validate中,检查是否仍然有效,否则返回false。

        【讨论】:

          猜你喜欢
          • 2014-02-11
          • 2017-02-26
          • 2015-03-12
          • 2021-11-04
          • 2019-11-19
          • 2015-11-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多