首先,这不是关于 如何获取用户的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_OnStart
在 Application_AuthenticateRequest
之前运行 - 所以它永远不会通过“经过身份验证”检查。
一个很好的例子是用户登录我的网站 - 使用Forms Auth,它设置一个过期日期为一周的cookie(例如)。
然后他们几天后回来 - Session_OnStart
被解雇了 - 但他们还没有通过身份验证。即使cookie存在 - 它还没有被处理到http上下文中。
所以Session_OnStart看起来像是不行 - 任何其他想法?
答案 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.Identity
和HttpContext.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与代码中的阻止列表进行比较,而不是数据库中的阻止列表。