如何取消验证当前用户 ASP.net mvc Identity
How to unauthenticate current user ASP.net mvc Identity
当用户访问我的网站时,他们会看到登录页面。一旦他们成功登录,他们就可以注销,另一个用户可以登录。但是,如果用户在登录时单击后退按钮,则会转到登录页面。此时新用户不能再登录。我收到 anti-forgery 令牌错误。
我已经尝试注销任何进入登录页面的用户。我尝试了不同的注销方式。我什至尝试 Session.Abandon();
账户管理员:
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
EnsureLoggedOut();
ViewBag.ReturnUrl = returnUrl;
// Store the originating URL so we can attach it to a form field
var viewModel = new LoginViewModel { ReturnUrl = returnUrl };
return View(viewModel);
}
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
ApplicationUser user = new ApplicationUser();
try
{
user = DBcontext.Users.Where(u => u.Email.Equals(model.Email)).Single(); // where db is ApplicationDbContext instance
}
catch (InvalidOperationException)
{
// the user is not exist
return View("The user does not exist.");
}
var result = await SignInManager.PasswordSignInAsync(user.UserName, model.Password, model.RememberMe, shouldLockout: false);
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);
}
}
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{ Session.Abandon();
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Index", "Home");
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}
}
登录查看:
@model LoginViewModel
@{ViewBag.PageId = "extr-page";
ViewBag.PageClass = "animated fadeInDown";}
@section topright{<span id="extr-page-header-space"> <span class="hidden-mobile">Need an account?</span> <a href="@Url.Action("register", "account")" class="btn btn-danger">Create account</a> </span>
}
<div id="content" class="container">
<div class="row">
@{ Html.RenderPartial("_LoginText"); }
<div class="col-xs-12 col-sm-12 col-md-5 col-lg-4">
<div class="well no-padding">
<form action="@Url.Action("Login", "Account")" method="POST" id="login-form" class="smart-form client-form">
<header>
Sign In
</header>
@Html.HiddenFor(m => m.ReturnUrl)
@Html.AntiForgeryToken()
@Html.ValidationBootstrap()
<fieldset>
<section>
<label class="label">E-mail</label>
<label class="input">
<i class="icon-append fa fa-user"></i>
<input type="Email" name="Email" value="demo@email.com">
<b class="tooltip tooltip-top-right"><i class="fa fa-user txt-color-teal"></i> Please enter email address/username</b>
</label>
</section>
<section>
<label class="label">Password</label>
<label class="input">
<i class="icon-append fa fa-lock"></i>
<input type="Password" name="Password" value="demo">
<b class="tooltip tooltip-top-right"><i class="fa fa-lock txt-color-teal"></i> Enter your password</b>
</label>
<div class="note">
<a href="@Url.Action("forgotpassword", "Account")"><i class="fa fa-frown-o"></i> Forgot password?</a>
</div>
</section>
<section>
<label class="checkbox">
<input type="checkbox" name="RememberMe" value="true" checked="checked">
<input type="hidden" name="RememberMe" value="false" />
<i></i>Stay signed in
</label>
</section>
</fieldset>
<footer>
<button type="submit" class="btn btn-primary">
Sign in
</button>
</footer>
</form>
</div>
@{ Html.RenderPartial("_SocialMedia"); }
</div>
</div>
我希望当用户点击后退按钮并 he/she 进入登录页面时,前一个用户会被注销。
更新 1:
需要明确的是,我并不担心刚刚注销并点击后退按钮的用户。相反,当用户成功登录然后点击后退按钮时,我的网站就会崩溃。它会将他们带回登录页面,但由于上述 Anti-Forgery 错误,用户名或密码无效。
更新二:
我在 IE 中测试了代码,没有问题。经过进一步研究,当我点击后退按钮时,Chrome 似乎正在保存身份验证 cookie。但是,当我正确注销时,cookie 会被销毁。我想当登录页面加载时我正在调用 LogOff 方法但它没有删除 cookie。我会继续研究这个问题。也许有人对此有经验?
更新 3:
我确实注意到当我点击后面的 btn 时 cookie 没有被删除。当我正确注销时,cookie 会被删除。当我不使用下面的 Shoe 方法缓存页面时,cookie 确实会在点击后退 btn 时被删除。但是,我仍然收到 anti-forgery 令牌错误。有趣的是,我在登录页面上弹出了 header 的一部分。 header 应该只在用户通过身份验证时出现。还应该有一个旁边的菜单来弹出身份验证。但事实并非如此。我想知道我是否有导致这两个问题的异步问题。
发生这种情况的原因可能是页面正在缓存,并且无法针对登录用户验证为匿名用户生成的 anti-forgery 令牌。
尝试在 Login
GET
上添加 OutputCache
(核心中的 ResponseCache
)属性,这将设置正确的 headers 不缓存页。
[OutputCache(NoStore = true, Duration = 0, Location = OutputCacheLocation.None)]
public ActionResult Login(string returnUrl)
{
...
}
我通过结合两件事解决了这个问题。
问题 1:
我注意到当我点击返回按钮并显示登录视图时,以前的用户 cookie 没有被破坏。这只发生在 chrome 而不是在 IE 中。这已通过我的 Login
Get
上的 [OutputCache(NoStore = true, Duration = 0, Location = OutputCacheLocation.None)]
属性解决(感谢@Shoe)。请参阅下面的代码。
登录:
// GET: /Account/Login
[AllowAnonymous]
[OutputCache(NoStore = true, Duration = 0, Location = OutputCacheLocation.None)]
public ActionResult Login(string returnUrl)
{
EnsureLoggedOut();
// Store the originating URL so we can attach it to a form field
var viewModel = new LoginViewModel { ReturnUrl = returnUrl };
return View(viewModel);
}
问题二:
第二个问题是,一旦显示登录视图,我调用了一个方法来使用 AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie, DefaultAuthenticationTypes.ExternalCookie);
和 Session.Abandon();
注销用户。在我出于不明白的原因点击刷新按钮之前,这并没有取消对用户的身份验证。直到我添加第二个步骤来清除主体以确保用户不会通过将 HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
添加到我的 EnsureLoggedOut 方法来保留任何身份验证。请参阅下面的代码。
EnsureLoggedOut 方法:
private void EnsureLoggedOut()
{
if (AuthenticationManager.User.Identity.IsAuthenticated)
{
//SignOut the current user
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie, DefaultAuthenticationTypes.ExternalCookie);
Session.Abandon();
// Second we clear the principal to ensure the user does not retain any authentication
HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
}
}
当用户访问我的网站时,他们会看到登录页面。一旦他们成功登录,他们就可以注销,另一个用户可以登录。但是,如果用户在登录时单击后退按钮,则会转到登录页面。此时新用户不能再登录。我收到 anti-forgery 令牌错误。
我已经尝试注销任何进入登录页面的用户。我尝试了不同的注销方式。我什至尝试 Session.Abandon();
账户管理员:
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
EnsureLoggedOut();
ViewBag.ReturnUrl = returnUrl;
// Store the originating URL so we can attach it to a form field
var viewModel = new LoginViewModel { ReturnUrl = returnUrl };
return View(viewModel);
}
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
return View(model);
}
ApplicationUser user = new ApplicationUser();
try
{
user = DBcontext.Users.Where(u => u.Email.Equals(model.Email)).Single(); // where db is ApplicationDbContext instance
}
catch (InvalidOperationException)
{
// the user is not exist
return View("The user does not exist.");
}
var result = await SignInManager.PasswordSignInAsync(user.UserName, model.Password, model.RememberMe, shouldLockout: false);
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);
}
}
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{ Session.Abandon();
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Index", "Home");
}
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}
}
登录查看:
@model LoginViewModel
@{ViewBag.PageId = "extr-page";
ViewBag.PageClass = "animated fadeInDown";}
@section topright{<span id="extr-page-header-space"> <span class="hidden-mobile">Need an account?</span> <a href="@Url.Action("register", "account")" class="btn btn-danger">Create account</a> </span>
}
<div id="content" class="container">
<div class="row">
@{ Html.RenderPartial("_LoginText"); }
<div class="col-xs-12 col-sm-12 col-md-5 col-lg-4">
<div class="well no-padding">
<form action="@Url.Action("Login", "Account")" method="POST" id="login-form" class="smart-form client-form">
<header>
Sign In
</header>
@Html.HiddenFor(m => m.ReturnUrl)
@Html.AntiForgeryToken()
@Html.ValidationBootstrap()
<fieldset>
<section>
<label class="label">E-mail</label>
<label class="input">
<i class="icon-append fa fa-user"></i>
<input type="Email" name="Email" value="demo@email.com">
<b class="tooltip tooltip-top-right"><i class="fa fa-user txt-color-teal"></i> Please enter email address/username</b>
</label>
</section>
<section>
<label class="label">Password</label>
<label class="input">
<i class="icon-append fa fa-lock"></i>
<input type="Password" name="Password" value="demo">
<b class="tooltip tooltip-top-right"><i class="fa fa-lock txt-color-teal"></i> Enter your password</b>
</label>
<div class="note">
<a href="@Url.Action("forgotpassword", "Account")"><i class="fa fa-frown-o"></i> Forgot password?</a>
</div>
</section>
<section>
<label class="checkbox">
<input type="checkbox" name="RememberMe" value="true" checked="checked">
<input type="hidden" name="RememberMe" value="false" />
<i></i>Stay signed in
</label>
</section>
</fieldset>
<footer>
<button type="submit" class="btn btn-primary">
Sign in
</button>
</footer>
</form>
</div>
@{ Html.RenderPartial("_SocialMedia"); }
</div>
</div>
我希望当用户点击后退按钮并 he/she 进入登录页面时,前一个用户会被注销。
更新 1: 需要明确的是,我并不担心刚刚注销并点击后退按钮的用户。相反,当用户成功登录然后点击后退按钮时,我的网站就会崩溃。它会将他们带回登录页面,但由于上述 Anti-Forgery 错误,用户名或密码无效。
更新二: 我在 IE 中测试了代码,没有问题。经过进一步研究,当我点击后退按钮时,Chrome 似乎正在保存身份验证 cookie。但是,当我正确注销时,cookie 会被销毁。我想当登录页面加载时我正在调用 LogOff 方法但它没有删除 cookie。我会继续研究这个问题。也许有人对此有经验?
更新 3: 我确实注意到当我点击后面的 btn 时 cookie 没有被删除。当我正确注销时,cookie 会被删除。当我不使用下面的 Shoe 方法缓存页面时,cookie 确实会在点击后退 btn 时被删除。但是,我仍然收到 anti-forgery 令牌错误。有趣的是,我在登录页面上弹出了 header 的一部分。 header 应该只在用户通过身份验证时出现。还应该有一个旁边的菜单来弹出身份验证。但事实并非如此。我想知道我是否有导致这两个问题的异步问题。
发生这种情况的原因可能是页面正在缓存,并且无法针对登录用户验证为匿名用户生成的 anti-forgery 令牌。
尝试在 Login
GET
上添加 OutputCache
(核心中的 ResponseCache
)属性,这将设置正确的 headers 不缓存页。
[OutputCache(NoStore = true, Duration = 0, Location = OutputCacheLocation.None)]
public ActionResult Login(string returnUrl)
{
...
}
我通过结合两件事解决了这个问题。
问题 1:
我注意到当我点击返回按钮并显示登录视图时,以前的用户 cookie 没有被破坏。这只发生在 chrome 而不是在 IE 中。这已通过我的 Login
Get
上的 [OutputCache(NoStore = true, Duration = 0, Location = OutputCacheLocation.None)]
属性解决(感谢@Shoe)。请参阅下面的代码。
登录:
// GET: /Account/Login
[AllowAnonymous]
[OutputCache(NoStore = true, Duration = 0, Location = OutputCacheLocation.None)]
public ActionResult Login(string returnUrl)
{
EnsureLoggedOut();
// Store the originating URL so we can attach it to a form field
var viewModel = new LoginViewModel { ReturnUrl = returnUrl };
return View(viewModel);
}
问题二:
第二个问题是,一旦显示登录视图,我调用了一个方法来使用 AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie, DefaultAuthenticationTypes.ExternalCookie);
和 Session.Abandon();
注销用户。在我出于不明白的原因点击刷新按钮之前,这并没有取消对用户的身份验证。直到我添加第二个步骤来清除主体以确保用户不会通过将 HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
添加到我的 EnsureLoggedOut 方法来保留任何身份验证。请参阅下面的代码。
EnsureLoggedOut 方法:
private void EnsureLoggedOut()
{
if (AuthenticationManager.User.Identity.IsAuthenticated)
{
//SignOut the current user
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie, DefaultAuthenticationTypes.ExternalCookie);
Session.Abandon();
// Second we clear the principal to ensure the user does not retain any authentication
HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
}
}