6

我做了一个功能,可以防止一个用户名同时登录多个,我在 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 并进入方法,它只是卡在那里并且不重定向:)

4

6 回答 6

4

我会从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 (Scope Controller)
  2. 前向订单 - OnActionExecuting : ActionFilter1 (Scope Global)
  3. 转发顺序 - OnActionExecuting : ActionFilter2 (Scope Controller)
  4. Forward Order - OnActionExecuting : ActionFilter3 (Scope Action)

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

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

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

更新

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

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

  // do other stuff with the Ajax Result
}

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

于 2013-08-08T22:48:20.087 回答
3

ActionFilterAttribute是要走的路。

于 2013-08-08T22:56:07.963 回答
2

我们创建了一个动作过滤器SeatCheck并像这样装饰每个控制器:

[SeatCheck]
public class NoteController : BaseController
{

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

在 proejctActionFilters文件夹中,我们有如下SeatCheck.cs所示的文件:

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

您可以像这样在操作过滤器中获取 SessionID

 filterContext.HttpContext.Session.SessionID
于 2013-08-08T22:46:54.660 回答
1

是的,确实有。您可以使用派生自 的属性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完整的数据库连接添加到每个操作中,而无需一行重复的代码。

于 2013-08-08T22:46:17.373 回答
1

创建一个自定义操作过滤器,并将该代码放入过滤器中,然后将过滤器应用于您的控制器。

于 2013-08-08T22:48:58.003 回答
0

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

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

于 2013-08-08T22:53:15.577 回答