仅在用户单击注销按钮后,如何保持用户登录系统并注销? [英] How to keep user login in to system and logout only after user clicks on logout button?
问题描述
我使用的是Microsoft asp.net身份的自定义实现,因为我具有自定义表,这就是为什么我为所有方法 IUserStore和IUserPasswordStore 给出了自定义实现的原因.
I am using custom implementation of microsoft asp.net identity because i have custom tables that is why i have given custom implementation of all my methods IUserStore and IUserPasswordStore.
问题是当用户登录时,然后在10到15分钟后登录用户 会话已过期,但除非用户注销,否则我要的是我要的 保持用户登录到系统.
Problem is when user logins then after 10 - 15 minutes login user session gets expired but what i want is unless user logs out i want to keep user login in to the system.
代码:
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
}
}
帐户控制器:
[Authorize]
public class AccountController : Controller
{
public AccountController()
: this(new UserManager<UserModel>(new UserStore()))
{
}
public AccountController(UserManager<UserModel> userManager)
{
UserManager = userManager;
}
public UserManager<UserModel> UserManager { get; private set; }
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(string email, string password, bool rememberMe = false, string returnUrl = null)
{
if (ModelState.IsValid)
{
var user = UserManager.Find(email, password);
if (user != null)
{
await SignInAsync(user, rememberMe);
return RedirectToLocal(returnUrl);
}
else
{
ModelState.AddModelError("", "Invalid username or password.");
}
}
return View();
}
private async Task SignInAsync(UserModel user, bool isPersistent)
{
var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
identity.AddClaim(new Claim("FullName", user.FirstName + " " + user.LastName));
identity.AddClaim(new Claim("Email", user.Email));
identity.AddClaim(new Claim("Role", user.Role));
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent, ExpiresUtc = DateTime.UtcNow.AddDays(7) }, identity);
}
private IAuthenticationManager AuthenticationManager
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
}
}
Web.config:
<system.web>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
</system.web>
<system.webServer>
<modules>
<remove name="FormsAuthentication" />
</modules>
</system.webServer>
现在在下面这行中,我给出了7天的到期时间,但会话仍在10-15分钟内过期:
Now in this below line i have given 7 days of expiry time but still sessions gets expires in 10 - 15 minutes:
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent, ExpiresUtc = DateTime.UtcNow.AddDays(7) }, identity);
在下面的问题中,您将找到我的 UserModel,自定义UserStore类,但是为了使这个问题小,我没有将代码放在此处:
Here in my below question you will find my UserModel,custom UserStore class but for keeping this question small i am not putting that code here:
更新:我已经完全排除了 ApplicationUser类,所以现在下面的代码对我来说是无用的,我想因此我的cookie过期了(我仍然是不确定):
Update:I have completely ruled out ApplicationUser class so now below code is useless for me and i think because of this my cookie gets expired i guess(still i am not sure):
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
}
注意:**长时间保持会话活动的原因是因为我的mvc应用程序是像Http get call,Http post call这样的角度驱动的,所以当用户会话过期时,我会尝试** get或post 调用,则在会话过期的情况下什么也不会发生,但是当我刷新整个页面时,用户将重定向到登录页面,但是直到那时,如果用户不刷新页面,用户将如何知道发生了什么.
Note:**The reason behind keeping session active for a long time is because my mvc application is angular driven like Http get call,Http post call so when user session gets expired and i if i try any **get or post call then nothing happens in case of session expires but when i refresh my whole page then user is redirected to login page but till then how user will know what is happening if user doesnot refresh the page.
推荐答案
您的问题是缺少SecurityStamp
.安全标记是一个随机字符串,用作检查服务器上的密码是否已更改.安全标记存储在cookie中,并且现在存储在每个cookie中,然后针对数据库进行检查.如果数据库(存储)中的值与cookie中的值不同-要求用户登录. SecurityStampValidator
正在执行所有检查和cookie无效操作.
Your problem is with lack of SecurityStamp
. Security stamp is a random string that works as check if password was changed on the server. Security stamp is stored in the cookie and every now-and then checked against the database. If value in the database (store) is different from the value in the cookie - user is asked to login. SecurityStampValidator
is doing all the checking and cookie invalidation.
您正在为用户使用自定义存储,这很好,但是您的存储未实现IUserSecurityStampStore
,并且当用户登录cookie时未获得SecurityStamp
的值.这导致SecurityStampValidator
的故障.
You are using a custom storage for users and that's fine, but your storage does not implement IUserSecurityStampStore
and when users login cookie is not getting a value of SecurityStamp
. This leads to a malfunction of SecurityStampValidator
.
因此,您的选择是:
- 在商店中实施
IUserSecurityStampStore
. - 从您的配置中删除
SecurityStampValidator
.
- Implement
IUserSecurityStampStore
in your store. - Remove
SecurityStampValidator
from your configuration.
由于安全问题,我不喜欢第二种选择.您希望您的用户永远保持登录状态-这意味着该cookie永远不会失效.但是,当用户有2个浏览器时,两者均已登录.并在其中一种浏览器中更改密码-第二种应注销并要求输入密码.如果不检查安全标记,第二个浏览器将不会注销,并且cookie仍然有效.现在,假设第二个浏览器在公用计算机上打开,用户忘记了注销-即使更改密码,也无法结束该会话.
I don't like the second option because of the security issue. You want your users to stay logged-in forever - that means the cookie is never invalidated. But when user have 2 browsers, both logged-in. And change password in one of the browsers - second should be logged-out and asked for the password. Without checking for security stamp second browser will not be logged-out and cookie will still be valid. Now imagine that second browser is opened on public computer and user forgot to log out - no way to end that session, even with password change.
要实现IUserSecurityStampStore
,请查看合同:
/// <summary>
/// Stores a user's security stamp
/// </summary>
/// <typeparam name="TUser"></typeparam>
/// <typeparam name="TKey"></typeparam>
public interface IUserSecurityStampStore<TUser, in TKey> : IUserStore<TUser, TKey> where TUser : class, IUser<TKey>
{
/// <summary>
/// Set the security stamp for the user
/// </summary>
/// <param name="user"></param>
/// <param name="stamp"></param>
/// <returns></returns>
Task SetSecurityStampAsync(TUser user, string stamp);
/// <summary>
/// Get the user security stamp
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
Task<string> GetSecurityStampAsync(TUser user);
}
基本上,这会在用户表中添加另一列:SecurityStamp
,您需要在其中保存一个字符串.标记的值是任何随机字符串. 默认身份实现(第734行左右)使用Guid.NewGuid().ToString()
-我建议您也这样做.
Basically this adds another column to your users table: SecurityStamp
and you need to save there a string. And value for the stamp is any random string. Default Identity implmenetation (around line 734) uses Guid.NewGuid().ToString()
- I suggest you do the same.
您的用户存储将如下所示:
Your user store will look something like this:
public class UserStore : IUserStore<UserModel>, IUserPasswordStore<UserModel>, IUserSecurityStampStore<TUser>
{
// your other methods
public async Task SetSecurityStampAsync(TUser user, string stamp)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
user.SecurityStamp = stamp;
return Task.FromResult(0);
}
Task<string> GetSecurityStampAsync(TUser user)
{
if (user == null)
{
throw new ArgumentNullException("user");
}
return Task.FromResult(user.SecurityStamp);
}
}
请记住-您无需在此操作中将用户保存到存储中. UserManager
在UpdateSecurityStampAsync
中为您执行此操作-除非您自己重写此方法.
Mind you - you don't need to save user into storage in this operation. UserManager
is doing this for you in UpdateSecurityStampAsync
- unless you override this method yourself.
创建新用户时,请不要忘记为SecurityStamp
字段分配值.并使用值更新所有现有用户.像这样的东西会起作用update MyUsersTable set SecurityStamp = convert(nvarchar(38), NewId())
Also don't forget to assign a value to SecurityStamp
field when create new users. And update all existing users with a value. Something like this will work update MyUsersTable set SecurityStamp = convert(nvarchar(38), NewId())
这篇关于仅在用户单击注销按钮后,如何保持用户登录系统并注销?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!