仅在用户单击注销按钮后,如何保持用户登录系统并注销? [英] How to keep user login in to system and logout only after user clicks on logout button?

查看:88
本文介绍了仅在用户单击注销按钮后,如何保持用户登录系统并注销?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用的是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:

UserModel和UserStore

更新:我已经完全排除了 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.

因此,您的选择是:

  1. 在商店中实施IUserSecurityStampStore.
  2. 从您的配置中删除SecurityStampValidator.
  1. Implement IUserSecurityStampStore in your store.
  2. 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);
    }
}

请记住-您无需在此操作中将用户保存到存储中. UserManagerUpdateSecurityStampAsync中为您执行此操作-除非您自己重写此方法.

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆