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

后端 未结 8 1484
傲寒
傲寒 2021-02-13 10:26

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 a

8条回答
  •  走了就别回头了
    2021-02-13 10:32

    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:

    /// 
    ///     Stores a user's security stamp
    /// 
    /// 
    /// 
    public interface IUserSecurityStampStore : IUserStore where TUser : class, IUser
    {
        /// 
        ///     Set the security stamp for the user
        /// 
        /// 
        /// 
        /// 
        Task SetSecurityStampAsync(TUser user, string stamp);
    
        /// 
        ///     Get the user security stamp
        /// 
        /// 
        /// 
        Task 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, IUserPasswordStore, IUserSecurityStampStore
    {
        // 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 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())

提交回复
热议问题