FormsAuthentication.SignOut () non disconnette l'utente


143

Mi sono rotto la testa un po 'troppo a lungo. Come posso impedire a un utente di sfogliare le pagine di un sito dopo che sono stati disconnessi utilizzando FormsAuthentication.SignOut? Mi aspetterei che questo lo faccia:

FormsAuthentication.SignOut();
Session.Abandon();
FormsAuthentication.RedirectToLoginPage();

Ma non lo fa. Se scrivo direttamente un URL, posso comunque accedere alla pagina. Non uso la sicurezza roll-your-own da un po ', quindi dimentico perché non funziona.


Quel codice va bene così com'è ... facendo clic di nuovo nel browser non si visita nuovamente la pagina sul server, ma si ricarica semplicemente la versione cache della pagina locale. Tutte le soluzioni seguenti sembrano ignorare questo fatto e in realtà non fanno nulla di più di quello che stai facendo qui. In breve ... non esiste una risposta a questa domanda che risolverà l'utente guardando la sua cache fino ad oggi, non credo che ci sia un modo per cancellare la cache in dire ... js o con un'istruzione sul lato server.
Guerra

Questa risposta offre alcuni modi per verificare, specialmente se il tuo sito non sta superando i test PEN: stackoverflow.com/questions/31565632/…
Tyler S. Loeper

Risposte:


211

Gli utenti possono comunque navigare sul tuo sito Web perché i cookie non vengono cancellati quando chiami FormsAuthentication.SignOut()e vengono autenticati su ogni nuova richiesta. Nella documentazione MS si dice che i cookie verranno cancellati ma non lo fanno, bug? È esattamente lo stesso conSession.Abandon() , il cookie è ancora lì.

È necessario modificare il codice in questo:

FormsAuthentication.SignOut();
Session.Abandon();

// clear authentication cookie
HttpCookie cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, "");
cookie1.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie1);

// clear session cookie (not necessary for your current problem but i would recommend you do it anyway)
SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState");
HttpCookie cookie2 = new HttpCookie(sessionStateSection.CookieName, "");
cookie2.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie2);

FormsAuthentication.RedirectToLoginPage();

HttpCookieè nello System.Webspazio dei nomi.Riferimento MSDN .


18
Questo funziona per me. Tuttavia, vale la pena notare che se la proprietà Domain è stata impostata sul cookie FormsAuthentication al momento dell'accesso, dovrà anche essere impostata alla scadenza del cookie al momento della disconnessione
Phil Hale,

8
Inoltre, non dimenticare cookie1.HttpOnly = true;
Dmitry Zaets,

6
Questa mi sembra una soluzione migliore: Response.Cookies [FormsAuthentication.FormsCookieName] .Expires = DateTime.Now.AddDays (-1);
Randy H.

7
@RandyH. Sovrascrivere il cookie di autenticazione Forms esistente con un nuovo cookie vuoto garantisce che anche se il client ripristina l'orologio di sistema, non sarà ancora in grado di recuperare i dati dell'utente dal cookie.
Tri Q Tran,

9
Qualcuno può combinare tutti questi commenti nella risposta?
David,

22

L'utilizzo di due dei post precedenti di x64igor e Phil Haselden ha risolto questo problema:

1. x64igor ha dato l'esempio per eseguire il logout:

  • Devi prima cancellare il cookie di autenticazione e il cookie di sessione restituendo i cookie vuoti nella risposta al logout.

    public ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        Session.Clear();  // This may not be needed -- but can't hurt
        Session.Abandon();
    
        // Clear authentication cookie
        HttpCookie rFormsCookie = new HttpCookie( FormsAuthentication.FormsCookieName, "" );
        rFormsCookie.Expires = DateTime.Now.AddYears( -1 );
        Response.Cookies.Add( rFormsCookie );
    
        // Clear session cookie 
        HttpCookie rSessionCookie = new HttpCookie( "ASP.NET_SessionId", "" );
        rSessionCookie.Expires = DateTime.Now.AddYears( -1 );
        Response.Cookies.Add( rSessionCookie );

2. Phil Haselden ha fornito l'esempio sopra di come prevenire la memorizzazione nella cache dopo il logout:

  • È necessario invalidare la cache sul lato client tramite la risposta .

        // Invalidate the Cache on the Client Side
        Response.Cache.SetCacheability( HttpCacheability.NoCache );
        Response.Cache.SetNoStore();
    
        // Redirect to the Home Page (that should be intercepted and redirected to the Login Page first)
        return RedirectToAction( "Index", "Home" ); 
    }

1
Ho perso un'intera giornata di lavoro per risolvere questo problema. Una volta effettuato l'accesso, il pulsante di logout ha iniziato a richiamare un'azione errata nel controller (Login non Logout). Grazie, questo ha risolto il problema. Ambiente di sviluppo: ASP.NET 4.51 MVC 5.1
Ako,

1
Buona risposta! Suggerimento Humble: Utilizzare la forma di cookies di sessione di compensazione x64igor utilizzato: SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState"); HttpCookie sessionCookie = new HttpCookie(sessionStateSection.CookieName, "");. In generale, il nome del cookie di sessione non lo è "ASP.NET_SessionId".
Seebiscuit,

20

Mi sembra che tu non abbia la sezione di autorizzazione web.config impostata correttamente. Vedi sotto per un esempio.

<authentication mode="Forms">
  <forms name="MyCookie" loginUrl="Login.aspx" protection="All" timeout="90" slidingExpiration="true"></forms>
</authentication>
<authorization>
  <deny users="?" />
</authorization>

Questa è una soluzione molto più semplice, la contrassegnerei come una risposta. Dato che una versione del codice funzionava su server diversi su una, non avevo bisogno di impostare proprietà aggiuntive aggiunte qui e su altre. Quindi modificare il codice non dovrebbe essere la soluzione corretta, modificare la configurazione è meglio.
Vladimir Bozic,

Per impostazione predefinita, slideExpiration è impostato su true ( msdn.microsoft.com/library/1d3t3c61(v=vs.100).aspx ). E ciò comporterà infine l'invalidità del cookie dopo x minuti come impostato nel timeout - e non quando l'utente viene disconnesso tramite SignOut (). Quindi questo non comporterà il comportamento desiderato per disconnettere un utente usando FormsAuthentication. Perfavore, correggimi se sbaglio.
OlafW

12

La chiave qui è che dici "Se scrivo direttamente un URL ...".

Per impostazione predefinita, sotto l'autenticazione basata su moduli il browser memorizza nella cache le pagine per l'utente. Quindi, selezionando un URL direttamente dal menu a discesa della casella dell'indirizzo del browser o digitandolo, PUO 'ottenere la pagina dalla cache del browser e non tornare mai al server per verificare l'autenticazione / autorizzazione. La soluzione a questo è impedire la memorizzazione nella cache sul lato client nell'evento Page_Load di ciascuna pagina o in OnLoad () della pagina di base:

Response.Cache.SetCacheability(HttpCacheability.NoCache);

Potresti anche chiamare:

Response.Cache.SetNoStore();

11

Ho già lottato con questo prima.

Ecco un'analogia per quello che sembra succedere ... Un nuovo visitatore, Joe, arriva al sito e accede tramite la pagina di accesso utilizzando FormsAuthentication. ASP.NET genera una nuova identità per Joe e gli fornisce un cookie. Quel biscotto è come la chiave di casa, e finché Joe ritorna con quella chiave, può aprire la serratura. Ad ogni visitatore viene data una nuova chiave e un nuovo lucchetto da usare.

Quando FormsAuthentication.SignOut()viene chiamato, il sistema dice a Joe di perdere la chiave. Normalmente funziona, dato che Joe non ha più la chiave, non può entrare.

Tuttavia, se Joe mai ritorna, e lo fa avere quella chiave perduta, egli è lasciato indietro in!

Da quello che posso dire, non c'è modo di dire ad ASP.NET di cambiare la serratura della porta!

Il modo in cui posso convivere con questo è ricordare il nome di Joe in una variabile Session. Quando si disconnette, abbandono la Sessione, quindi non ho più il suo nome. Più tardi, per verificare se gli è consentito, ho semplicemente confrontato la sua identità. Nome a quello che ha la sessione corrente, e se non corrispondono, non è un visitatore valido.

In breve, per un sito Web, NON fare affidamento User.Identity.IsAuthenticatedsenza controllare anche le variabili di sessione!


8
+ 1, penso che questo si chiami "attacco di replay dei cookie". C'è un articolo sulle limitazioni di FormsAuthentication.SignOut: support.microsoft.com/kb/900111
Dmitry

3
Per chiunque cerchi di seguire il link sopra, è morto. Puoi provare a utilizzare WaybackMachine per ottenere una copia di questa pagina qui, ma IMMEDIATAMENTE cerca di reindirizzare l'utente. web.archive.org/web/20171128133421/https://…
killa-byte

7

Dopo molte ricerche finalmente questo ha funzionato per me. Spero possa essere d'aiuto.

public ActionResult LogOff()
{
    AuthenticationManager.SignOut();
    HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
    return RedirectToAction("Index", "Home");
}

<li class="page-scroll">@Html.ActionLink("Log off", "LogOff", "Account")</li>

Ho sviluppato applicazioni web per anni in PHP. Quindi sono nuovo di MVC ... Ammetto di amarlo, MA chi avrebbe pensato a qualcosa di così semplice come disconnettere qualcuno sarebbe stato così difficile? Ho provato tutti gli altri script su questa pagina e questo è l'unico che ha funzionato. Grazie per la pubblicazione!
Anthony Griggs,

6

Questo funziona per me

public virtual ActionResult LogOff()
    {
        FormsAuthentication.SignOut();
        foreach (var cookie in Request.Cookies.AllKeys)
        {
            Request.Cookies.Remove(cookie);
        }
        foreach (var cookie in Response.Cookies.AllKeys)
        {
            Response.Cookies.Remove(cookie);
        }
        return RedirectToAction(MVC.Home.Index());
    }

3

Il codice che hai pubblicato sembra che dovrebbe rimuovere correttamente il token di autenticazione dei moduli, quindi è possibile che le cartelle / pagine in questione non siano effettivamente protette.

Hai confermato che non è possibile accedere alle pagine prima che si sia verificato un accesso?

Puoi pubblicare le impostazioni web.config e il codice di accesso che stai utilizzando?


3

Ho scritto una classe di base per tutte le mie pagine e ho riscontrato lo stesso problema. Avevo un codice simile al seguente e non funzionava. Tramite la traccia, il controllo passa dall'istruzione RedirectToLoginPage () alla riga successiva senza essere reindirizzato.

if (_requiresAuthentication)
{
    if (!User.Identity.IsAuthenticated)
        FormsAuthentication.RedirectToLoginPage();

    // check authorization for restricted pages only
    if (_isRestrictedPage) AuthorizePageAndButtons();
}

Ho scoperto che ci sono due soluzioni. O per modificare FormsAuthentication.RedirectToLoginPage (); essere

if (!User.Identity.IsAuthenticated)
    Response.Redirect(FormsAuthentication.LoginUrl);

O per modificare web.config aggiungendo

<authorization>
  <deny users="?" />
</authorization>

Nel secondo caso, durante la traccia, il controllo non ha raggiunto la pagina richiesta. È stato reindirizzato immediatamente all'URL di accesso prima di colpire il punto di interruzione. Quindi, il metodo SignOut () non è il problema, il metodo di reindirizzamento è quello.

Spero che possa aiutare qualcuno

Saluti


2
Inoltre potresti chiamare Response.End () subito dopo aver chiamato FormsAuthentication.RedirectToLoginPage ()
murki,

Penso che ci sia un po 'di cattiva comunicazione da parte di MS. Devi bloccare le persone se vuoi che vengano restituite alla pagina di accesso. Altrimenti il ​​framework ti consentirà con gioia l'accesso. Quindi devi dire la soluzione n. 2 in questo post.
Josh Robinson,

3

Ho appena provato alcuni dei suggerimenti qui e mentre sono stato in grado di utilizzare il pulsante Indietro del browser, quando ho fatto clic su una selezione di menu il token [Autorizza] per quel [ActionResult] mi ha riportato alla schermata di accesso.

Ecco il mio codice di disconnessione:

        FormsAuthentication.SignOut();
        Response.Cookies.Remove(FormsAuthentication.FormsCookieName);
        Response.Cache.SetExpires(DateTime.Now.AddSeconds(-1));
        HttpCookie cookie = HttpContext.Request.Cookies[FormsAuthentication.FormsCookieName];
        if (cookie != null)
        {
            cookie.Expires = DateTime.Now.AddDays(-1);
            Response.Cookies.Add(cookie);
        }

Anche se la funzione back sul browser mi ha riportato indietro e visualizzato il menu protetto (ci sto ancora lavorando) non sono stato in grado di fare nulla che fosse protetto nell'app.

Spero che questo ti aiuti


Grazie. Questa è la soluzione che ha funzionato per me (non è necessario <deny users="?" />in web.config)
Alexei,

3

Ho provato la maggior parte delle risposte in questa discussione, senza fortuna. Finito con questo:

protected void btnLogout_Click(object sender, EventArgs e)
{
    FormsAuthentication.Initialize();
    var fat = new FormsAuthenticationTicket(1, "", DateTime.Now, DateTime.Now.AddMinutes(-30), false, string.Empty, FormsAuthentication.FormsCookiePath);
    Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(fat)));
    FormsAuthentication.RedirectToLoginPage();
}

Trovato qui: http://forums.asp.net/t/1306526.aspx/1


3

Questa risposta è tecnicamente identica a Khosro.Pakmanesh. Lo sto postando per chiarire in che modo la sua risposta differisce dalle altre risposte su questa discussione e in quale caso può essere utilizzata.

In generale per cancellare una sessione utente, facendo

HttpContext.Session.Abandon();
FormsAuthentication.SignOut();

disconnetterà efficacemente l'utente. Tuttavia , se nella stessa richiesta è necessario controllare Request.isAuthenticated(come spesso può accadere in un filtro di autorizzazione, ad esempio), si troverà che

Request.isAuthenticated == true

anche _dopo aver fatto HttpContext.Session.Abandon()e FormsAuthentication.SignOut().

L'unica cosa che ha funzionato è stata fare

AuthenticationManager.SignOut();
HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);

Questo imposta efficacemente Request.isAuthenticated = false.


2

Questo ha iniziato succedendo a me quando ho impostato l'authentication> forme> proprietà Percorso in Web.config. La rimozione risolveva il problema e un semplice FormsAuthentication.SignOut();rimuoveva nuovamente il cookie.


1

È possibile che tu stia effettuando l'accesso da un sottodominio (sub1.dominio.com) e quindi provando a disconnettersi da un altro sottodominio (www.dominio.com).


1

Ho appena avuto lo stesso problema, in cui SignOut () apparentemente non è riuscito a rimuovere correttamente il ticket. Ma solo in un caso specifico, in cui un'altra logica ha causato un reindirizzamento. Dopo aver rimosso questo secondo reindirizzamento (sostituito con un messaggio di errore), il problema è scomparso.

Il problema deve essere stato che la pagina è stata reindirizzata al momento sbagliato, quindi non ha innescato l'autenticazione.


1

Sto riscontrando un problema simile ora e credo che il problema nel mio caso, così come il poster originale, sia dovuto al reindirizzamento. Per impostazione predefinita, un Response.Redirect provoca un'eccezione che bolle immediatamente fino a quando non viene rilevato e il reindirizzamento viene immediatamente eseguito, suppongo che ciò impedisca il trasferimento della raccolta di cookie modificata al client. Se modifichi il codice per utilizzare:

Response.Redirect("url", false);

Ciò impedisce l'eccezione e sembra consentire il corretto invio del cookie al client.


1

Basta provare a inviare una variabile di sessione quando si preme il login. E nella pagina di benvenuto, verificare innanzitutto se quella sessione è vuota in questo modo nel caricamento della pagina o nell'evento Init:

if(Session["UserID"] == null || Session["UserID"] == "")
{
    Response.Redirect("Login.aspx");
}

1

Per me, il seguente approccio funziona. Penso che se ci sono errori dopo l'istruzione "FormsAuthentication.SignOut ()", SingOut non funziona.

public ActionResult SignOut()
    {
        if (Request.IsAuthenticated)
        {
            FormsAuthentication.SignOut();

            return Redirect("~/");
        }
        return View();
     }

0

Stai testando / vedendo questo comportamento usando IE? È possibile che IE stia offrendo quelle pagine dalla cache. È notoriamente difficile convincere IE a svuotare la sua cache, e quindi in molte occasioni, anche dopo aver effettuato il logout, digitando l'URL di una delle pagine "protette" mostrerebbe il contenuto memorizzato nella cache di prima.

(Ho visto questo comportamento anche quando accedi come un altro utente e IE mostra la barra "Benvenuto" nella parte superiore della tua pagina, con il nome utente dell'utente precedente. Oggi, un ricaricamento lo aggiornerà, ma se è persistente , potrebbe essere ancora un problema di memorizzazione nella cache.)


0

Fare Session.abandon () e distruggere il cookie funziona piuttosto bene. Sto usando mvc3 e sembra che il problema si verifichi se vai su una pagina protetta, esci e vai nella cronologia del tuo browser. Non è un grosso problema ma è comunque un po 'fastidioso.

Cercare di passare attraverso i collegamenti sulla mia app Web funziona comunque nel modo giusto.

L'impostazione per non eseguire la memorizzazione nella cache del browser potrebbe essere la strada da percorrere.


0

Per MVC questo funziona per me:

        public ActionResult LogOff()
        {
            FormsAuthentication.SignOut();
            return Redirect(FormsAuthentication.GetRedirectUrl(User.Identity.Name, true));
        }

0

Volevo aggiungere alcune informazioni per aiutare a capire il problema. L'autenticazione basata su moduli consente di archiviare i dati dell'utente in un cookie o nella stringa di query dell'URL. Il metodo supportato dal tuo sito può essere configurato nel file web.config.

Secondo Microsoft :

Il metodo SignOut rimuove le informazioni del ticket di autenticazione dei moduli dal cookie o dall'URL se CookiesSupported è falso .

Allo stesso tempo, dicono :

Uno dei valori di HttpCookieMode che indica se l'applicazione è configurata per l'autenticazione con moduli senza cookie. L' impostazione predefinita è UseDeviceProfile .

Infine, per quanto riguarda UseDeviceProfile, dicono :

Se la proprietà CookieMode è impostata su UseDeviceProfile, la proprietà CookiesSupported restituirà true se il browser per la richiesta corrente supporta entrambi i cookie e il reindirizzamento con i cookie ; in caso contrario, la proprietà CookiesSupported restituirà false.

Mettendo tutto questo insieme, a seconda del browser dell'utente, la configurazione predefinita può comportare che Cookie Support sia vero , il che significa che il metodo SignOut non cancella il ticket dal cookie. Sembra controintuitivo e non so perché funzioni in questo modo - mi aspetto che SignOut disconnetta l'utente in qualsiasi circostanza.

Un modo per far funzionare SignOut da solo è cambiare la modalità dei cookie in "UseCookies" (ovvero i cookie sono obbligatori) nel file web.config:

<authentication mode="Forms">
  <forms loginUrl="~/Account/SignIn" cookieless="UseCookies"/>
</authentication>

Secondo i miei test, facendo ciò SignOut funziona da solo a spese del tuo sito e ora richiede i cookie per funzionare correttamente.


Penso che tu stia leggendo quello sbagliato. Per quanto riguarda SignOut (), sono abbastanza sicuro che ciò che significano è che verrà cancellato dall'URL se CookiesSupported è falso, altrimenti dal cookie. Vale a dire che avrebbero dovuto scrivere "Il metodo SignOut rimuove le informazioni del ticket di autenticazione dei moduli dal cookie o, se CookiesSupported è falso, dall'URL."
Oskar Berggren,

-1

Tieni presente che WIF rifiuta di dire al browser di ripulire i cookie se il messaggio wsignoutcleanup di STS non corrisponde all'URL con il nome dell'applicazione di IIS e intendo CASE SENSITIVE . WIF risponde con il segno di spunta verde OK, ma non invierà il comando per eliminare i cookie dal browser.

Quindi, devi prestare attenzione alla distinzione tra maiuscole e minuscole del tuo url.

Ad esempio, ThinkTecture Identity Server salva gli URL dei RP visitanti in un cookie, ma li rende tutti minuscoli. WIF riceverà il messaggio wsignoutcleanup in minuscolo e lo confronterà con il nome dell'applicazione in IIS. Se non corrisponde, non elimina i cookie, ma segnalerà OK al browser. Quindi, per questo Identity Server avevo bisogno di scrivere tutti gli URL in web.config e tutti i nomi delle applicazioni in IIS in lettere minuscole, al fine di evitare tali problemi.

Inoltre, non dimenticare di consentire i cookie di terze parti nel browser se hai applicazioni al di fuori del sottodominio di STS, altrimenti il ​​browser non eliminerà i cookie anche se WIF glielo dice.


1
WIF? STS? ThinkTecture Identity Server? Che cosa sono tutte queste cose e come si riferiscono a questa domanda?
Oskar Berggren,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.