How to keep user login in to system and logout only after user clicks on logout button?

一笑奈何 提交于 2019-12-09 14:35:00

问题


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.

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.

Code:

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));
        }
    }

Account Controller:

[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>

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);

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 and UserStore

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))
                }
            });            
 }

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.


回答1:


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.

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.

So your options are:

  1. Implement IUserSecurityStampStore in your store.
  2. Remove SecurityStampValidator from your configuration.

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.

To implement IUserSecurityStampStore look on the contract:

/// <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);
}

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);
    }
}

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.

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())




回答2:


I had the same problem and I was really confused because without any reason user was redirected to login page means that he wasn't authorized. I had changed the timeout to more than 8 hours but nothing was changed. After reading many pages such as Aspnet unexpected logout or frequent-unexpected-user-logoff I found that something is wrong with the machine key and after checking machine key in web.config file I could detect the problem with machine key. By changing the machine key and make it the same with others in Owin section everything is working well.




回答3:


Have you tried

 ExpireTimeSpan = TimeSpan.FromDays(7);

so this would make your code:

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))
                }
            });

            ExpireTimeSpan = TimeSpan.FromDays(7);
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
            app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
        }
    }



回答4:


You should also configure the session timeout at the application pool level in IIS as it is described here: https://technet.microsoft.com/en-us/library/cc771956(v=ws.10).aspx




回答5:


call controller method at specific time interval so it will reset session timeout on evry call. For example if initially you have set your session timeout at 30 min and after 20 min you call this action, it will reset session timeout to again 30 min, by this way your session remains active even reach 30 min after login.

Place your JQuery code in layout

JQuery:

var RefreshSessionInterval;

$(document).ready(function () {        
      clearInterval(RefreshSessionInterval);
      RefreshSessionInterval = setInterval("RefreshSession()", 30000);  // change your interval time as per requirement     
});

function RefreshSession() {
       $.ajax({
            type: "POST",
            url: '@Url.Action("RefreshSession", "YourControllerName")',           
            success: function (data) {               
            },
            error: function () {
            }
       }); 
}

Controller:

Public void RefreshSession()
{
    //your session reset from this line, as i know you don't have to write any code here.
}

public bool LogOut()
{
        LogOff();
        return true;
}

void LogOut()
{       
    Session.Clear();
    Session.Abandon();
    Session.RemoveAll();
    ClearCache();        
}

void ClearCache()
{
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
    Response.Cache.SetExpires(DateTime.Now.AddSeconds(-1));
    Response.Cache.SetNoStore();
    ////FormsAuthentication.SignOut();
}



回答6:


Here's what i did when i coded a user to keep signed in...

Code

public partial class Startup
    {

        public void ConfigureAuth(IAppBuilder app)
        {
            // Enable the application to use a cookie to store information for the signed in user
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                LoginPath = new PathString("/Account/Login")
            });
// Use a cookie to temporarily store information about a user logging in with a third party login provider
            app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        }
    }

Account Controller

public class AccountController : Controller
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="AccountController"/> class.
        /// </summary>
        public AccountController()
            : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext())))
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="AccountController"/> class.
        /// </summary>
        /// <param name="userManager">The user manager.</param>
        public AccountController(UserManager<ApplicationUser> userManager)
        {
            UserManager = userManager;
        }

        /// <summary>
        /// Gets the user manager.
        /// </summary>
        /// <value>
        /// The user manager.
        /// </value>
        public UserManager<ApplicationUser> UserManager { get; private set; }

        //
        // GET: /Account/Login
        /// <summary>
        /// Logins the specified return URL.
        /// </summary>
        /// <param name="returnUrl">The return URL.</param>
        /// <returns></returns>
        [AllowAnonymous]
        public ActionResult Login(string returnUrl)
        {
            ViewBag.ReturnUrl = returnUrl;
            return View();
        }

        //
        // POST: /Account/Login
        /// <summary>
        /// Logins the specified model.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="returnUrl">The return URL.</param>
        /// <returns></returns>
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                var user = await UserManager.FindAsync(model.UserName, model.Password);
                if (user != null)
                {
                    await SignInAsync(user, model.RememberMe);
                    return RedirectToLocal(returnUrl);
                }
                else
                {
                    ModelState.AddModelError("", "Invalid username or password.");
                }
            }

            // If we got this far, something failed, redisplay form
            return View(model);
        }

private async Task SignInAsync(ApplicationUser user, bool isPersistent)
        {
            AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
            var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
            AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
        }

OR.. You can also configure the session timeout for a user at the application pool level in IIS.




回答7:


Session lifetime (how long until the session disappears) and authentication lifetime (how long until the user has to login again) are two separate and distinct timeframes.

If authentication lifetime is longer than session timeframe this means that a session will start with the user already authenticated (i.e. the user will not need to login to initiate a session).

If authentication lifetime is shorter than session timeframe this means a user will be forced to login before their session expires. I'm not certain whether the session is "refreshed" when the user reauthenticates (as a guess... probably).

Just setting very long expirations for session and authentication might not be a piratical production ready solution (i.e. there's lots of ways for sessions to "disappear").

Why do you care if the user's session disappears and then a new one is started (without the user having to login)? Without a bit more information about what you're intending I can't really understand the core of your question.




回答8:


Examine the settings of the forms element within the authentication element of your web.config file.

Note the default values for the two applicable settings.

  1. timeout (default is 30 minutes)
  2. slidingExpiration (True or False / default varines with .NET Framework version)

For your situation, you will probably want a timeout duration much higher than 30 minutes and a slidingExpiration value of True.



来源:https://stackoverflow.com/questions/39588718/how-to-keep-user-login-in-to-system-and-logout-only-after-user-clicks-on-logout

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!