我看到你正在使用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))
}
});
复现步骤:
validateInterval: TimeSpan.FromMinutes(30)
更改为validateInterval: TimeSpan.FromSeconds(1)
UserManager.UpdateSecurityStampAsync
方法调用。user.HasResetPassword = true;
user.IsFirstLoginAfterPasswordReset = false;
步骤二
在Models文件夹中添加一个名为AuthHandler.cs的类,并实现以下内容。 在这个阶段,您需要验证用户是否已重置密码并且自密码重置以来尚未首次登录。如果是,则将用户重定向到登录页面。
第三步
在RouteConfig.cs中调用AuthHandler,以便为应用程序的每个传入HTTP请求调用它。
第四步
在ResetPassword方法中添加以下实现。在此步骤中,当用户重置密码时,更新属性以表示他们已重置密码并且尚未首次登录。请注意,当用户重置密码时,他们也会明确退出登录。
步骤 5
在登录方法中添加以下实现。在此步骤中,如果用户成功登录,则验证其密码是否已重置,并且首次登录为 false。如果所有条件都为真,则更新数据库中的属性,以便当用户在将来重置密码时,属性处于准备就绪的状态。因此,这是一种循环确定和更新密码重置和首次登录后重置密码的状态。
最后评论
这是我处理它的方式。我还没有测试过,如果遇到异常,你可能需要修改它。它也是硬编码的,以展示解决问题的方法。
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));
}
基于CodeRealm的回答...
对于任何遇到浏览器访问应用程序时抛出空指针异常(即对象引用未设置为对象的实例)的情况,这是因为您的数据库中可能存在HasResetPassWord和/或IsFirstLoginAfterPasswordReset为空的记录。HTTP请求将正常工作,但HTTPS请求将失败,原因不明。
解决方案:只需手动更新数据库并为两个字段赋值。最好在两个列上都设置为false。
ASP.NET Identity身份验证依赖于用户浏览器上的cookie。因为您使用了两个不同的浏览器进行测试,所以您将获得两个不同的身份验证cookie。在cookie过期之前,用户仍然被认证,这就是您得到这些结果的原因。
因此,您需要进行一些自定义实现。
例如,始终检查用户是否已重置密码并且尚未使用新密码首次登录。如果他们没有,请注销并重定向到登录页面。当他们登录时,将创建一个新的auth cookie。
我基于Github博客上的这篇文章来设计我的方法。
他们使用了一个混合Cookie存储/数据库方法,使用Ruby编写,但我将其移植到了我的ASP .Net MVC项目中,并且运行良好。
用户可以查看所有其他会话并在需要时撤销它们。当用户重置密码时,任何活动会话都将被撤销。
我在基础控制器上使用了一个来检查活动会话cookie。如果发现会话cookie已过期,则用户将被注销并重定向到登录页面。