关于跟踪用户“最后的IP地址”的建议

时间:2011-05-06 05:41:06

标签: asp.net-mvc session ip-address

首先,这不是关于 如何获取用户的IP地址的问题,因为我知道如何做到这一点。

基本上,我的网站管理员(ASP.NET MVC 3 Web应用程序)需要能够阻止某个IP地址提交用户内容。所以我在我们的系统中存储了对用户的“IP地址”。凉。

我的问题是:

  

我应该检查用户当前的IP地址并保存到数据库中(例如,在什么时候,页面生命周期事件)?

目前,我正在考虑使用会话。也就是说,当我第一次创建会话时(例如Session_OnStart()),抓住用户的IP地址并将其粘贴在会话中。然后当会话结束时(例如Session_OnEnd()),我看到会话中的IP地址是否与数据库的IP地址不同。如果是,请更新数据库。

目前我们正在使用 InProc ,但我们很有可能会在稍后进入 StateServer - 并且MSDN声明 Session_OnEnd 仅适用于 InProc 。这可能是一个问题。

这种方法的任何想法/替代方案?

修改

所以我尝试使用Session_OnStart()尝试执行以下操作:

  

如果用户已通过身份验证,请获取其IP地址,从数据库中获取最后一个IP,如果不同,请更新数据库。

但问题似乎是Session_OnStartApplication_AuthenticateRequest之前运行 - 所以它永远不会通过“经过身份验证”检查。

一个很好的例子是用户登录我的网站 - 使用Forms Auth,它设置一个过期日期为一周的cookie(例如)。

然后他们几天后回来 - Session_OnStart被解雇了 - 但他们还没有通过身份验证。即使cookie存在 - 它还没有被处理到http上下文中。

所以Session_OnStart看起来像是不行 - 任何其他想法?

4 个答案:

答案 0 :(得分:1)

只是在会话开始时而不是结束时记录ip会有问题吗?就像你说的那样,ip在会话期间不会改变。

答案 1 :(得分:1)

接受了@ lomaxx的答案 - 但我想我会为其他人添加自己的答案以及为什么需要这样做的原因。

解决方案:对每个请求执行全局操作过滤器。

(简体)代码:

public class UserTrackingFilterAttribute : ActionFilterAttribute
{
    public override void OnResultExecuted(ResultExecutedContext filterContext)
    {
        // If the user isn't authenticated or we have already tracked IP this session, bubble back up to base context.
        if (!Authenticated || HaveTrackedIpAddressThisSession)
        {
            base.OnResultExecuted(filterContext);
            return;
        }

        // Get the users current ip address.
        var currentIp = HttpContext.Current.Request.CurrentIpAddress(); // extension method to read server variables, cater for proxy, etc

        // Get the users last known ip address from repository.
        var userService = ObjectFactory.GetInstance(typeof(IUserService)) as IUserService;
        var unitOfWork = ObjectFactory.GetInstance(typeof(IUnitOfWork)) as IUnitOfWork;
        if (userService == null || unitOfWork == null) return;

        // See if the user's ip has changed.
        var currentUser = userService.FindById(CurrentUserId);
        if (currentUser == null || (currentUser.LastIpAddress != null && IPAddress.Parse(currentUser.LastIpAddress).Equals(currentIp)))
        {
            // User cannot be found or IP hasn't changed - set session key and bubble back up to base context.
            HaveTrackedIpAddressThisSession = true;
            base.OnResultExecuted(filterContext);
            return;
        }

        // User's ip has changed - update ip address.
        currentUser.LastIpAddress = currentIp.ToString();

        // Save.
        userService.Save(currentUser);

        // Commit.
        unitOfWork.Commit();

        // Update session key.
        HaveTrackedIpAddressThisSession = true;
    }
}

“CurrentUserId”和“HaveTrackedIpAddressThisSession”是缩短该方法中代码的私有属性。基本上他们分别阅读HttpContext.Current.User.IdentityHttpContext.Current.Session["someKey"]

为什么我需要对Global.asax事件进行全局操作过滤:因为我的逻辑需要Http主体存在,我不能使用Session_OnStart,因为那时,表单身份验证cookie尚未解密为主体身份。因此,虽然这会在每个页面请求上运行,但会话“标志”可以减轻这种开销。

答案 2 :(得分:0)

由于它是asp.net MVC并且你希望它针对所有请求运行,我会考虑使用像这里描述的那样的全局动作过滤器http://weblogs.asp.net/gunnarpeipman/archive/2010/08/15/asp-net-mvc-3-global-action-filters.aspx

答案 3 :(得分:0)

我猜你正在数据库中进行比较,阅读你的问题。

我的建议是将阻止列表保存在web.config中以简化操作,并在需要时将传入的ID与它进行比较。

如果您需要将阻止列表保留在数据库中,我会说使用缓存类将阻止列表缓存一段合理的时间,并将ip与代码中的阻止列表进行比较,而不是数据库中的阻止列表。