Ответ 1
Решение несколько проще, чем вы начали внедрять. Но идея такая же: каждый раз, когда пользователь входит в систему, измените свою марку безопасности. И это приведет к аннулированию всех других сеансов входа в систему. Таким образом, мы научим пользователей не делиться своим паролем.
Я только что создал новое приложение MVC5 из стандартного шаблона VS2013 и успешно выполнил то, что вы хотите сделать.
Метод входа. Вам нужно изменить штамп безопасности перед созданием файла cookie, поскольку после того, как файл cookie установлен, вы не можете легко обновить значения:
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
// check if username/password pair match.
var loggedinUser = await UserManager.FindAsync(model.Email, model.Password);
if (loggedinUser != null)
{
// change the security stamp only on correct username/password
await UserManager.UpdateSecurityStampAsync(loggedinUser.Id);
}
// do sign-in
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Invalid login attempt.");
return View(model);
}
}
Таким образом, каждый логин будет делать обновление в записи пользователя с новой маркой безопасности. Обновление штампа безопасности - это всего лишь вопрос await UserManager.UpdateSecurityStampAsync(user.Id);
- гораздо проще, чем вы предполагали.
Следующий шаг - проверить печать безопасности по каждому запросу. Вы уже нашли лучший крючок в Startup.Auth.cs
, но вы снова слишком сложны. Рамка уже делает то, что вам нужно сделать, вам нужно немного ее настроить:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
// other stuff
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(0), // <-- Note the timer is set for zero
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
Временной интервал установлен на ноль - означает, что фреймворк в каждом запросе будет сравнивать печать безопасности пользователя с базой данных. Если штамп в cookie не совпадает с штампом в базе данных, пользовательское auth-cookie выкидывается, прося его выйти из системы.
Однако обратите внимание, что это будет иметь дополнительный запрос к вашей базе данных по каждому запросу HTTP от пользователя. На большой базе пользователей это может быть дорогостоящим, и вы можете несколько увеличить интервал проверки до пары минут - это даст вам меньше запросов к вашей базе данных, но все равно будет нести ваше сообщение о том, что вы не будете делиться регистрационными данными.