web-dev-qa-db-fra.com

Comment garder l'utilisateur connecté au système et se déconnecter uniquement lorsque l'utilisateur clique sur le bouton de déconnexion?

J'utilise une implémentation personnalisée de l'identité de Microsoft asp.net parce que j'ai des tableaux personnalisés, c'est pourquoi j'ai donné une implémentation personnalisée de toutes mes méthodes IUserStore et IUserPasswordStore .

Le problème est lorsque les connexions utilisateur, puis après 10 à 15 minutes de session de connexion utilisateur, ont expiré, mais ce que je veux, c'est à moins que l'utilisateur ne se déconnecte, je souhaite conserver la connexion de l'utilisateur au système.

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

Contrôleur de compte:

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

Maintenant, dans cette ligne ci-dessous, j'ai donné 7 jours de délai d'expiration, mais les sessions expirent dans 10 à 15 minutes:

  AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent, ExpiresUtc = DateTime.UtcNow.AddDays(7) }, identity);

Ici, dans ma question ci-dessous, vous trouverez mon UserModel, classe personnalisée UserStore mais pour garder cette question petite, je ne mets pas ce code ici:

UserModel et UserStore

Mise à jour : J'ai complètement exclu Classe ApplicationUser donc le code ci-dessous est inutile pour moi et je pense que, pour cette raison, mon cookie est périmé, je suppose (je ne suis toujours pas sûr):

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

Remarque: ** La raison pour laquelle la session reste active pendant longtemps est que mon application mvc est pilotée de manière angulaire, comme un appel get Http, un appel Http ainsi, lorsque la session de l'utilisateur expire, et si j'essaie n'importe quel ** get ou post call alors rien ne se passe en cas d'expiration de la session, mais lorsque j'actualise la totalité de la page, l'utilisateur est redirigé vers la page de connexion, mais jusqu'à ce que l'utilisateur sache ce qui se passe s'il ne rafraîchit pas la page.

Votre problème est avec le manque de SecurityStamp. Le timbre de sécurité est une chaîne aléatoire qui vérifie si le mot de passe a été modifié sur le serveur. Timbre de sécurité est stocké dans le cookie et chaque maintenant, puis vérifié par rapport à la base de données. Si la valeur dans la base de données (magasin) est différente de la valeur dans le cookie, il est demandé à l'utilisateur de se connecter. SecurityStampValidator effectue toutes les vérifications et l’invalidation des cookies.

Vous utilisez un stockage personnalisé pour les utilisateurs et c'est correct, mais votre stockage ne met pas en œuvre IUserSecurityStampStore et lorsque le cookie de connexion des utilisateurs ne reçoit pas la valeur SecurityStamp. Cela conduit à un dysfonctionnement de SecurityStampValidator

Donc, vos options sont:

  1. Implémentez IUserSecurityStampStore dans votre magasin.
  2. Supprimez SecurityStampValidator de votre configuration.

Je n'aime pas la deuxième option à cause du problème de sécurité. Vous voulez que vos utilisateurs restent connectés pour toujours - cela signifie que le cookie n'est jamais invalidé. Mais lorsque l'utilisateur dispose de 2 navigateurs, tous deux connectés. Et changez le mot de passe dans l'un des navigateurs - le deuxième doit être déconnecté et le mot de passe demandé Sans vérification du cachet de sécurité, le second navigateur ne sera pas déconnecté et le cookie sera toujours valide. Imaginez maintenant que le second navigateur est ouvert sur un ordinateur public et que l'utilisateur a oublié de se déconnecter - aucun moyen de mettre fin à cette session, même avec un changement de mot de passe.

Pour mettre en œuvre IUserSecurityStampStore, consultez le contrat :

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

En gros, cela ajoute une autre colonne à votre table d'utilisateurs: SecurityStamp et vous devez y enregistrer une chaîne. Et la valeur pour le tampon est une chaîne quelconque. Implémentation de l'identité par défaut (autour de la ligne 734) utilise Guid.NewGuid().ToString() - Je vous suggère de faire de même.

Votre magasin utilisateur ressemblera à ceci: 

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

Remarquez, vous n'avez pas besoin de sauvegarder l'utilisateur dans la mémoire lors de cette opération. UserManager le fait pour vous dans UpdateSecurityStampAsync - sauf si vous substituez cette méthode vous-même.

N'oubliez pas non plus d'attribuer une valeur au champ SecurityStamp lors de la création de nouveaux utilisateurs. Et mettre à jour tous les utilisateurs existants avec une valeur. Quelque chose comme ça fonctionnera update MyUsersTable set SecurityStamp = convert(nvarchar(38), NewId())

3
trailmax

La méthode du contrôleur d’appel à un intervalle de temps spécifique afin de réinitialiser le délai de session lors de chaque appel. Par exemple, si vous avez initialement défini le délai d'expiration de votre session sur 30 min et que vous appelez cette action après 20 min, le délai d'expiration de la session sera réinitialisé à 30 min. De cette manière, votre session reste active et atteint même 30 min après la connexion.

Placez votre code JQuery dans la mise en page

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

Manette:

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

J'ai eu le même problème et j'étais vraiment confus parce que, sans aucune raison, l'utilisateur a été redirigé vers la page de connexion, ce qui signifie qu'il n'était pas autorisé. J'avais changé le délai d'attente à plus de 8 heures mais rien n'a été changé. Après avoir lu de nombreuses pages telles que déconnexion imprévue Aspnet ou déconnexion fréquente-inattendue-utilisateur - j'ai constaté un problème avec la clé d'ordinateur et, après vérification de la clé d'ordinateur dans le fichier web.config, j'ai pu détecter le problème. avec clé machine. En changeant la clé de machine et en faisant la même chose avec les autres dans la section Owin, tout fonctionne bien.

3
Mohsen Shadman

As-tu essayé 

 ExpireTimeSpan = TimeSpan.FromDays(7);

donc cela ferait votre 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));
        }
    }
3
ShufflerShark

Vous devez également configurer le délai d'expiration de session au niveau du pool d'applications dans IIS tel qu'il est décrit ici: https://technet.Microsoft.com/en-us/library/cc771956(v=ws. 10) .aspx

2
Trifon

Voici ce que j'ai fait quand j'ai codé un utilisateur pour qu'il reste connecté ...

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

        }
    }

Contrôleur de compte

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

OU .. Vous pouvez également configurer le délai de session d'un utilisateur au niveau du pool d'applications dans IIS.

1
Yaman Ahlawat

La durée de vie de la session (combien de temps jusqu'à ce que la session disparaisse) et la durée de l'authentification (combien de temps jusqu'à ce que l'utilisateur doive se reconnecter) sont deux périodes distinctes et distinctes.

Si la durée de vie de l'authentification est supérieure à la durée de la session, cela signifie qu'une session commencera avec l'utilisateur déjà authentifié (c'est-à-dire que l'utilisateur n'aura pas besoin de se connecter pour lancer une session).

Si la durée de vie de l'authentification est plus courte que la durée de la session, cela signifie qu'un utilisateur sera obligé de se connecter avant l'expiration de sa session. Je ne suis pas certain que la session soit "rafraîchie" lorsque l'utilisateur se réauthentifiera (probablement ...).

Définir des expirations très longues pour la session et l’authentification peut ne pas être une solution prête à la production piratée (c’est-à-dire qu’il existe de nombreux moyens pour que les sessions "disparaissent").

Pourquoi vous souciez-vous de la disparition de la session de l'utilisateur et de la création d'une nouvelle (sans que l'utilisateur n'ait à se connecter)? Sans un peu plus d'informations sur vos intentions, je ne peux pas vraiment comprendre l'essentiel de votre question.

0
user2845090

Examinez les paramètres de l’élément forms au sein de l’élément authentication, de votre fichier web.config.

Notez les valeurs par défaut pour les deux paramètres applicables.

  1. délai d'attente (30 minutes par défaut)
  2. slideExpiration (Varines True ou False/default avec la version .NET Framework)

Dans votre cas, vous souhaiterez probablement une durée de dépassement de temps bien supérieure à 30 minutes et une valeur de slideExpiration de True.

0
JohnH