Sha*_*hin 23 cookies asp.net-mvc asp.net-identity
我试图在我的应用程序中使用相同的用户阻止多次登录.
我的想法是在用户登录时更新安全标记并将其添加为Claim,然后在每个请求中将Cookie中的标记与数据库中的标记进行比较.这就是我实现的方式:
public virtual async Task<ActionResult> Login([Bind(Include = "Email,Password,RememberMe")] LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
SignInStatus result =
await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, false);
switch (result)
{
case SignInStatus.Success:
var user = UserManager.FindByEmail(model.Email);
var id = user.Id;
UserManager.UpdateSecurityStamp(user.Id);
var securityStamp = UserManager.FindByEmail(model.Email).SecurityStamp;
UserManager.AddClaim(id, new Claim("SecurityStamp", securityStamp));
Run Code Online (Sandbox Code Playgroud)
然后在身份验证配置中我添加了
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = ctx =>
{
var ret = Task.Run(() =>
{
Claim claim = ctx.Identity.FindFirst("SecurityStamp");
if (claim != null)
{
var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()));
var user = userManager.FindById(ctx.Identity.GetUserId());
// invalidate session, if SecurityStamp has changed
if (user != null && user.SecurityStamp != null && user.SecurityStamp != claim.Value)
{
ctx.RejectIdentity();
}
}
});
return ret;
}
}
});
Run Code Online (Sandbox Code Playgroud)
如图所示,我试图将cookie中的声明与数据库中的声明进行比较,如果它们不相同则拒绝身份.
现在,每次用户登录时,安全标记都会更新,但用户的cookie中的值不同,我找不到原因?我怀疑是否新的更新安全标记不会存储在用户的cookie中?
tra*_*max 25
解决方案比您开始实施时更简单.但这个想法是一样的:每次用户登录时,都要更改他们的安全标记.这将使所有其他登录会话无效.这样就会教用户不要分享他们的密码.
我刚刚从标准的VS2013模板创建了一个新的MVC5应用程序,并成功地实现了您想要的功能.
登录方式.您需要在创建auth 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);
}
}
Run Code Online (Sandbox Code Playgroud)
这样,每次登录都会使用新的安全标记对用户记录进行更新.更新安全标记只是一个问题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))
}
});
Run Code Online (Sandbox Code Playgroud)
时间间隔设置为零 - 表示每个请求上的框架将比较用户的安全戳和数据库.如果cookie中的戳记与数据库中的戳记不匹配,则会抛出用户的auth-cookie,要求他们注销.
但是,请注意,这将在用户的每个HTTP请求上向您的数据库发出额外请求.在庞大的用户基础上,这可能会很昂贵,您可以将检查间隔时间增加到几分钟 - 这样可以减少对数据库的请求,但仍会传递有关不共享登录详细信息的消息.
| 归档时间: |
|
| 查看次数: |
8534 次 |
| 最近记录: |