当密码更改时,从所有浏览器注销用户

时间:2016-02-05 09:53:25

标签: asp.net asp.net-mvc authentication cookies iis-8.5

我有一个重设密码页面:enter image description here

当用户填写详细信息并单击Reset Password按钮时。调用以下控制器:

public ActionResult ResetPassword(ResetPassword model)
{
    ...
    return RedirectToAction("Logout");
}

当用户更改密码时,他们会从浏览器中获取Logged Out。但是,如果他们同时登录到另一个浏览器,他们仍然会在另一个浏览器上登录。

我想在用户更改密码时从登录的所有浏览器中注销用户。

6 个答案:

答案 0 :(得分:12)

我看到您正在使用ASP.NET Identity 2.您正在尝试做的事情已经内置。您需要做的就是更改 SecurityStamp ,之前的所有身份验证Cookie都不再有效

更改密码后,您还需要更改 SecurityStamp

await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
await UserManager.UpdateSecurityStampAsync(User.Identity.GetUserId());

如果您希望用户保持登录状态,则必须重新签发新的身份验证Cookie(登录):

    await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);

否则,启动密码更改的用户/会话也将被注销。

要立即注销所有其他会话,您需要降低配置中的检查间隔:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login"),
    Provider = new CookieAuthenticationProvider
    {
        // Enables the application to validate the security stamp when the user logs in.
        // This is a security feature which is used when you change a password or add an external login to your account.  
        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
            validateInterval: TimeSpan.FromSeconds(1),
            regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
    }
});

重现的步骤:

  1. 在VS2015中创建了一个新的Asp.Net Web App。
  2. 选择MVC模板。
  3. 编辑 App_Stat / Startup.Auth.cs ,第34行:将validateInterval: TimeSpan.FromMinutes(30)更改为validateInterval: TimeSpan.FromSeconds(1)
  4. 编辑 Controllers / ManageController.cs ,第236行:添加UserManager.UpdateSecurityStampAsync方法调用。
  5. 运行项目,创建用户,登录,打开其他浏览器并登录。
  6. 更改密码,在其他浏览器中刷新页面:您应该已注销。

答案 1 :(得分:4)

所以我回到家并决定整理一些代码。告诉我代码!!!

我会使用一个处理程序,因此验证总是在用户首次访问应用程序时完成,并且在一个地方为每个操作方法访问完成。

这个想法是当用户重置密码时,应用程序记录用户重置密码并且第一次没有登录并注销用户。

user.HasResetPassword = true;
user.IsFirstLoginAfterPasswordReset = false;

当用户登录时,应用程序会验证用户之前是否已重置密码,并且现在是第一次登录。如果这些陈述有效,则应用程序会更新其记录,表明您没有重置密码,而且您没有首次登录。

第1步

向ApplicationUser模型添加两个属性

enter image description here

第2步

在Models文件夹中添加一个AuthHandler.cs类,其实现如下。 在此阶段,您将验证用户是否已重置密码,并且自密码重置以来第一次未登录。如果是这样,请将用户重定向到登录名。

enter image description here

第3步

在RouteConfig.cs中调用AuthHandler,以便为每个传入的应用程序的http请求调用它。 enter image description here

第4步

在ResetPassword方法中添加如下实现。在此步骤中,当用户重置其密码更新属性时,他们已重置密码并且尚未首次登录。请注意,用户在重置密码时也会明确注销。

enter image description here

第5步

在Login方法中添加以下实现。在此步骤中,如果用户成功登录,请验证其密码是否已重置且第一次记录的密码为false。如果满足所有条件,请更新数据库中的属性,以便在用户以后重置密码时,属性处于准备状态。所以有一种循环确定并更新密码重置的状态,并在重置密码后首次登录。

enter image description here

<强>最后

您的AspnetUsers表应如下所示

enter image description here

<强>评论

这就是我接近它的方法。我没有对它进行测试,因此如果遇到异常,您可能会对其进行修改。所有这些都是硬编码,以显示解决问题的方法。

答案 2 :(得分:0)

ASP.NET身份验证依赖于用户浏览器上的cookie。因为您使用两个不同的浏览器来测试它。您将拥有两个不同的身份验证cookie。如果Cookie过期,用户仍然会通过身份验证这就是您获得该结果的原因。

所以你必须提供一些自定义实现。

例如,始终检查用户是否已重置密码,并且尚未使用新密码首次登录。如果他们没有,请将其退出并重定向到登录。当他们登录时,将创建新的身份验证cookie。

答案 3 :(得分:0)

我根据Github的博客

对我的方法进行了建模

Modeling your App's User Session

他们使用Hybrid Cookie Store / DB approach使用ruby,但我将其移植到My ASP .Net MVC项目并且工作正常。

用户可以查看所有其他会话,并在需要时撤消它们。当用户重置密码时,任何活动会话都将被撤销。

我在基本控制器上使用ActionFilterAttribute来检查活动会话cookie。如果发现会话cookie过时,则用户将被注销并重定向以登录。

答案 4 :(得分:0)

基于CodeRealm的答案......

对于遇到https访问浏览器上的应用程序的情况的任何人抛出空指针异常(即对象引用未设置为对象的实例),这是因为数据库中可能存在现有记录HasResetPassWord和/或IsFirstLoginAfterPasswordReset为null。 Http请求将起作用,但https请求将失败,不确定原因。

解决方案:只需手动更新数据库并提供两个字段值。两列优选地为假。

答案 5 :(得分:0)

甚至ASP.NET身份验证清楚地表明您必须进行二次检查以确认用户是否仍是活动登录用户(例如,我们可以阻止用户,用户可能已更改其密码),表单身份验证票证不会对这些事情提供任何保障。

UserSession与ASP.NET MVC Session无关,它只是一个名字

我实施的解决方案是,

  1. 使用UserSessions
  2. 在数据库中创建UserSessionID (PK, Identity) UserID (FK) DateCreated, DateUpdated
  3. FormsAuthenticationTicket有一个名为UserData的字段,您可以将UserSessionID保存在其中。
  4. 用户登录时

    public void DoLogin(){
    
         // do not call this ...
         // FormsAuthentication.SetAuthCookie(....
    
         DateTime dateIssued = DateTime.UtcNow;
    
         var sessionID = db.CreateSession(UserID);
         var ticket = new FormsAuthenticationTicket(
                userName,
                dateIssued,
                dateIssued.Add(FormsAuthentication.Timeout),
                iSpersistent,
                // userData
                sessionID.ToString());
    
         HttpCookie cookie = new HttpCookie(
             FormsAuthentication.CookieName,
             FormsAuthentication.Encrypt(ticket));
         cookie.Expires = ticket.Expires;
         if(FormsAuthentication.CookieDomain!=null)
             cookie.Domain = FormsAuthentication.CookieDomain;
         cookie.Path = FormsAuthentication.CookiePath;
         Response.Cookies.Add(cookie);
    
    }
    

    授权用户

    Global.asax类可以挂钩授权

    public void Application_Authorize(object sender, EventArgs e){
         var user = Context.User;
         if(user == null)   
             return;
    
         FormsIdentity formsIdentity = user.Identity as FormsIdentity;
         long userSessionID = long.Parse(formsIdentity.UserData);
    
         string cacheKey = "US-" + userSessionID;
    
         // caching to improve performance
         object result = HttpRuntime.Cache[cacheKey];
         if(result!=null){
             // if we had cached that user is alright, we return..
             return;
         }
    
         // hit the database and check if session is alright
         // If user has logged out, then all UserSessions should have been
         // deleted for this user
         UserSession session = db.UserSessions
               .FirstOrDefault(x=>x.UserSessionID == userSessionID);
         if(session != null){
    
              // update session and mark last date
              // this helps you in tracking and you
              // can also delete sessions which were not
              // updated since long time...
              session.DateUpdated = DateTime.UtcNow;
              db.SaveChanges();
    
              // ok user is good to login
              HttpRuntime.Cache.Add(cacheKey, "OK", 
                   // set expiration for 5 mins
                   DateTime.UtcNow.AddMinutes(5)..)
    
             // I am setting cache for 5 mins to avoid
             // hitting database for all session validation
             return;
         }
    
         // ok validation is wrong....
    
    
         throw new UnauthorizedException("Access denied");
    
    }
    

    用户退出时

    public void Logout(){
    
        // get the ticket..
        FormsIdentity f = Context.User.Identity as FormsIdentity;
        long sessionID = long.Parse(f.UserData);
    
        // this will prevent cookie hijacking
        var session = db.UserSessions.First(x=>x.UserSessionID = sessionID);
        db.UserSession.Remove(session);
        db.SaveChanges();
    
        FormsAuthentication.Signout();
    }
    

    当用户更改密码或用户被阻止或用户被删除时......

    public void ChangePassword(){
    
        // get the ticket..
        FormsIdentity f = Context.User.Identity as FormsIdentity;
        long sessionID = long.Parse(f.UserData);
    
        // deleting Session will prevent all saved tickets from
        // logging in
        db.Database.ExecuteSql(
            "DELETE FROM UerSessions WHERE UserSessionID=@SID",
            new SqlParameter("@SID", sessionID));
    }