FormsAuthentication.SignOut () не виходить з користувача


143

Трохи задовго бив головою проти цього. Як я заважаю користувачеві переглядати сторінки сайту після того, як вони вийшли з системи за допомогою FormsAuthentication.SignOut? Я б очікував, що це зробить:

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

Але це не так. Якщо я введу URL-адресу безпосередньо, я все ще можу перейти на сторінку. Я деякий час не використовував власну безпеку, тому я забув, чому це не працює.


Цей код добре, як і ... натискання назад у браузері не переглядає сторінку на сервері, вона просто перезавантажує локальну кешовану версію сторінки. Всі рішення нижче, здається, ігнорують цей факт і насправді не роблять нічого більше, ніж ви робите тут. Коротше кажучи ... немає відповіді на це питання, яка вирішить користувача, дивлячись на їх кеш на сьогоднішній день, я не вірю, що є спосіб очистити кеш у скажімо ... js або за допомогою серверної інструкції.
Війна

Ця відповідь пропонує декілька способів перевірити, особливо якщо на вашому веб-сайті не вистачає тестів PEN: stackoverflow.com/questions/31565632/…
Тайлер С. Лопер,

Відповіді:


211

Користувачі все ще можуть переглядати ваш веб-сайт, оскільки файли cookie не видаляються під час дзвінка, FormsAuthentication.SignOut()і вони автентифікуються при кожному новому запиті. У документації на MS сказано, що файли cookie будуть очищені, але вони цього не роблять, помилка? Це точно так само Session.Abandon(), печиво все ще є.

Ви повинні змінити свій код на це:

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знаходиться в System.Webпросторі імен. Довідник MSDN .


18
Це працює для мене. Однак варто зазначити, що якщо властивість Domain було встановлено на файлі cookie FormsAuthentication під час входу, його також потрібно буде встановити під час виходу файлу cookie під час виходу з нього
Phil Hale

8
Також не забувайте cookie1.HttpOnly = true;
Дмитро Заєць

6
Мені це здається кращим рішенням: Response.Cookies [FormsAuthentication.FormsCookieName] .Expires = DateTime.Now.AddDays (-1);
Ренді Х.

7
@RandyH. Перезапис існуючого файлу cookie FormsAuthentication новим порожнім файлом cookie гарантує, що навіть якщо клієнт поверне системний годинник, він все одно не зможе отримати будь-які дані користувача з файлу cookie.
Tri Q Tran

9
Чи може хтось поєднати всі ці коментарі у відповідь?
Девід

22

Використовуючи дві вищезгадані публікації від x64igor та Філа Хасельдена, вирішили це:

1. x64igor наводив приклад робити Вихід:

  • Спочатку потрібно очистити Cookie- файли автентифікації та cookie-сеанси , повернувши порожні файли cookie у відповіді на вихід.

    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. Філ Хасельден наводив приклад вище, як запобігти кешування після виходу з системи:

  • Потрібно визначити недійсним кеш на стороні клієнта через відповідь .

        // 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
Витрачений цілий день на роботі, щоб вирішити цю проблему. Після входу у систему кнопки виходу почали викликати неправильні дії в контролері (Login not Logout). Дякую, це вирішило проблему. Навколишнє середовище розвитку: ASP.NET 4.51 MVC 5.1
Ako

1
Хороша відповідь! Humble пропозицію: Використовуйте Форму для очищення сесійних куків x64igor використовується: SessionStateSection sessionStateSection = (SessionStateSection)WebConfigurationManager.GetSection("system.web/sessionState"); HttpCookie sessionCookie = new HttpCookie(sessionStateSection.CookieName, "");. Загалом назва файлу cookie сеансу - ні "ASP.NET_SessionId".
seebiscuit

20

Мені звучить так, що у вас не встановлено належним чином налаштований розділ авторизації web.config. Дивіться приклад нижче.

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

Це набагато простіше рішення, я б зазначив це як відповідь. Оскільки я отримав одну версію коду, що працює на різних серверах, на одному мені не потрібно було встановлювати додаткові властивості, які ви додали тут, а на інших - я. Отже, модифікація коду не повинна бути правильним рішенням, краще змінювати конфігурацію.
Володимир Бозич

За замовчуванням ковзанняExpiration встановлено на true ( msdn.microsoft.com/library/1d3t3c61(v=vs.100).aspx ). І це, нарешті, призведе до того, що файл cookie стає недійсним через х хвилин, як встановлено в тайм-ауті, а не тоді, коли користувач вийде з системи SignOut (). Таким чином, це не призведе до бажаної поведінки, щоб вимкнути користувача за допомогою FormsAuthentication. Будь ласка, виправте мене, якщо я помиляюся.
OlafW

12

Ключовим тут є те, що ви говорите "Якщо я введу URL-адресу безпосередньо ...".

За замовчуванням під автентифікацією форм браузер кешує сторінки для користувача. Отже, вибираючи URL-адресу безпосередньо з розкритого списку адрес браузера або вводячи її, МОЖЕ отримати сторінку з кешу браузера і ніколи не повертатися на сервер, щоб перевірити автентифікацію / авторизацію. Рішення цього полягає в тому, щоб запобігти кешування на стороні клієнта в події Page_Load кожної сторінки або в OnLoad () базової сторінки:

Response.Cache.SetCacheability(HttpCacheability.NoCache);

Ви також можете подзвонити:

Response.Cache.SetNoStore();

11

Я боровся з цим і раніше.

Ось аналогія того, що, здається, відбувається ... Новий відвідувач, Джо, приходить на сайт і входить через сторінку входу за допомогою FormsAuthentication. ASP.NET створює нову особистість для Джо і дає йому печиво. Це печиво - як ключ від будинку, і поки Джо повернеться з цим ключем, він може відкрити замок. Кожному відвідувачеві надається новий ключ та новий замок для використання.

Коли FormsAuthentication.SignOut()викликається, система повідомляє Джо втратити ключ. Зазвичай це працює, оскільки у Джо більше немає ключа, він не може потрапити.

Однак, якщо Джо коли - небудь повернеться, і робить є що втрачений ключ, він нехай назад!

З того, що я можу сказати, немає способу сказати ASP.NET змінити замок на дверях!

Я можу з цим жити - запам’ятати ім’я Джо в змінній Session. Коли він виходить із системи, я відмовляюся від сесії, тому більше не маю його імені. Пізніше, щоб перевірити, чи він дозволений, я просто порівнюю його Identity.Name з тим, що має поточний сеанс, і якщо вони не відповідають, він не є дійсним відвідувачем.

Коротше кажучи, для веб-сайту НЕ покладайтеся на те, User.Identity.IsAuthenticatedне перевіряючи також свої змінні сесії!


8
+ 1, я думаю, це називається "атака повторного використання файлів cookie". Є стаття про обмеження FormsAuthentication.SignOut: support.microsoft.com/kb/900111
Дмитро

3
Для тих, хто хоче перейти за посиланням вище, він мертвий. Ви можете спробувати використати WaybackMachine, щоб отримати копію цієї сторінки тут, але вона НЕМОВНО намагається перенаправити користувача. web.archive.org/web/20171128133421/https://…
killa-byte

7

Після багатьох пошуків нарешті це спрацювало для мене. Я сподіваюся, що це допомагає.

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>

Я роками розробляв веб-додатки на PHP. Тож я новачок у MVC ... Зізнаюся, мені це подобається, Але хто б міг подумати про щось таке просте, як вийти з когось, було б так складно? Я спробував будь-який інший скрипт на цій сторінці вниз, і це єдиний, хто працював. Дякуємо за публікацію!
Ентоні Гріггс

6

Це працює для мене

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

Опублікований вами код виглядає так, що він повинен правильно видалити маркер автентифікації форм, тому можливо, що папки / сторінки, про які йде мова, фактично не захищені.

Ви підтвердили, що до сторінок не можна отримати доступ до того, як відбудеться вхід?

Чи можете ви розмістити налаштування web.config та код входу, який ви використовуєте?


3

Я писав базовий клас для всіх своїх Сторінок, і я прийшов до того ж питання. У мене був такий код, як наступний, і він не працював. Відстежуючи, управління переходить від оператора RedirectToLoginPage () до наступного рядка без перенаправлення.

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

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

Я з’ясував, що є два рішення. Або для зміни FormsAuthentication.RedirectToLoginPage (); бути

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

АБО щоб змінити web.config, додавши

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

У другому випадку під час відстеження контроль не дійшов до потрібної сторінки. Він був перенаправлений негайно до URL-адреси входу, перш ніж потрапити на точку перерви. Отже, метод SignOut () не проблема, метод переадресації - один.

Я сподіваюся, що це може комусь допомогти

З повагою


2
Також ви можете зателефонувати Response.End () відразу після виклику FormsAuthentication.RedirectToLoginPage ()
murki

Я думаю, що з боку MS є непогане спілкування. Ви повинні заблокувати людей, якщо ви хочете, щоб вони повернулися на сторінку входу. Інакше рамка радісно дозволить вам отримати доступ. Тож вам слід сказати рішення №2 у цій публікації.
Джош Робінсон

3

Я просто спробував деякі пропозиції тут, і, коли я зміг скористатися кнопкою повернення браузера, коли натиснув на вибір меню, [Авторизувати] маркер для цього [ActionResult] відправив мене прямо на екран входу.

Ось мій вихідний код:

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

Хоча функція "назад" у браузері повернула мене і показала захищене меню (я над цим працюю), я не зміг зробити нічого, що було захищено в додатку.

Сподіваюся, це допомагає


Дякую. Це рішення, яке працювало на мене (не потрібно <deny users="?" />в web.config)
Олексій

3

Я спробував більшість відповідей у ​​цій темі, не пощастило. Закінчилося це:

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

Знайдено його тут: http://forums.asp.net/t/1306526.aspx/1


3

Ця відповідь технічно ідентична Хосро.Пакманеш. Я розміщую його, щоб уточнити, чим його відповідь відрізняється від інших відповідей на цій темі, і в якому випадку використання її можна використовувати.

Взагалі, щоб очистити сеанс користувача, робити

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

буде ефективно виходити з користувача. Однак якщо у тому самому запиті вам потрібно перевірити Request.isAuthenticated(як це часто трапляється, наприклад, у фільтрі авторизації), то ви виявите, що

Request.isAuthenticated == true

навіть після того, як ви це зробили HttpContext.Session.Abandon()і FormsAuthentication.SignOut().

Єдине, що працювало, це робило

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

Це ефективно встановлює Request.isAuthenticated = false.


2

Це почалося зі мною, коли я встановив аутентифікацію> форми> властивість Path у Web.config. Видалення, яке вирішило проблему, і просте FormsAuthentication.SignOut();знову видалили файл cookie.


1

Можливо, ви входите з одного піддомену (sub1.domain.com), а потім намагаєтесь вийти з іншого піддомену (www.domain.com).


1

У мене просто була та сама проблема, коли SignOut (), здавалося б, не вдалося належним чином видалити квиток. Але лише в конкретному випадку, коли якась інша логіка викликала переспрямування. Після того, як я видалив цю другу переспрямування (замінив її повідомленням про помилку), проблема усунулася.

Проблема, мабуть, полягала в тому, що сторінку перенаправляли в неправильний час, отже, не викликали автентифікацію.


1

У мене зараз подібне питання, і я вважаю, що проблема в моєму випадку, як і оригінальний плакат, пов’язана з перенаправленням. За замовчуванням Response.Redirect спричиняє виняток, який негайно перетворюється на бульбашку, поки не буде спійманий, і переадресація негайно виконується, я здогадуюсь, що це перешкоджає передачі зміненої колекції файлів до клієнта. Якщо ви змінили код для використання:

Response.Redirect("url", false);

Це запобігає виключенню і, здається, дозволяє cookie належним чином відправити назад клієнту.


1

Просто спробуйте надіслати змінну сеансу, коли ви натискаєте увійти. А на вітальній сторінці спочатку перевірте, чи такий сеанс порожній, як це під час завантаження сторінки чи в події Init:

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

1

Для мене працює наступний підхід. Я думаю, якщо після заяви "FormsAuthentication.SignOut ()" є помилка, SingOut не працює.

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

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

0

Ви тестуєте / бачите цю поведінку за допомогою IE? Можливо, що IE подає ці сторінки з кеша. Як відомо, важко змусити IE очистити кеш-пам'ять, і тому в багатьох випадках навіть після виходу з системи введення URL однієї із "захищених" сторінок відображатиме кешований вміст раніше.

(Я бачив цю поведінку, навіть коли ви реєструєтесь як інший користувач, і IE показує панель привітання у верхній частині вашої сторінки з ім’ям старого користувача. Сьогодні зазвичай перезавантаження оновить його, але якщо воно є стійким , це все ще може бути проблемою кешування.)


0

Робота Session.abandon () та знищення файлу cookie працює дуже добре. Я використовую mvc3, і схоже, що проблема виникає, якщо ви переходите на захищену сторінку, виходите з системи та переходите через історію свого браузера. Не велика справа, але все одно щось дратує.

Спроба пройти посилання на моєму веб-додатку працює правильним шляхом.

Налаштування не робити кешування браузера може бути дорогою.


0

Для MVC це працює для мене:

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

0

Я хотів додати трохи інформації, щоб допомогти зрозуміти проблему. Аутентифікація форм дозволяє зберігати дані користувача або у файлі cookie, або в рядку запиту URL-адреси. Спосіб, який підтримує ваш сайт, можна налаштувати у файлі web.config.

За даними Microsoft :

Метод SignOut видаляє інформацію про білети форми автентичності з файлу cookie чи URL-адреси, якщо CookiesSupported неправдиві .

При цьому вони кажуть :

Одне з значень HttpCookieMode, яке вказує, чи налаштовано додаток для аутентифікації форм cookie. За замовчуванням використовується UseDeviceProfile .

Нарешті, щодо UseDeviceProfile вони кажуть :

Якщо для властивості CookieMode встановлено UseDeviceProfile, властивість CookiesSupported повернеться істинною, якщо браузер для поточного Запиту підтримує як cookie, так і перенаправлення з cookie ; в іншому випадку властивість CookiesSupported поверне помилкове значення.

Розділяючи це все разом, залежно від браузера користувача, конфігурація за замовчуванням може призвести до того, що CookiesSupported є істинними , що , а це означає, що метод SignOut не очищає квиток від файлу cookie. Це здається контрінтуїтивним, і я не знаю, чому це працює таким чином - я би сподівався, що SignOut насправді вийде за будь-яких обставин користувача.

Один із способів змусити роботу SignOut самостійно - змінити режим cookie на "UseCookies" (тобто файли cookie потрібні) у файлі web.config:

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

Згідно з моїми тестами, це робить SignOut самостійно за рахунок вашого веб-сайту, і тепер для його роботи потрібні файли cookie.


Я думаю, ти читаєш це неправильно. Щодо SignOut (), я майже впевнений, що вони означають, що він буде очищений від URL-адреси, якщо CookiesSupported неправдиві, інакше від файлу cookie. Тобто вони повинні були написати "Метод SignOut видаляє інформацію про білети форми автентифікації з файлу cookie або, якщо CookiesSupported неправдивий, з URL-адреси."
Оскар Берггрен

-1

Майте на увазі, що WIF відмовляється повідомити браузеру очистити файли cookie, якщо повідомлення wsignoutcleanup від STS не відповідає URL-адресі з назвою програми від IIS, і я маю на увазі CASE SENSITIVE . WIF відповідає зеленим прапором OK, але не надсилає команду видалити куки в браузер.

Отже, вам потрібно звернути увагу на чутливість регістру вашої URL-адреси.

Наприклад, сервер ThinkTecture Identity зберігає URL-адреси відвідуючих RP-файлів в одному файлі cookie, але це робить їх усі малі регістри. WIF отримає повідомлення wsignoutcleanup в нижньому регістрі і порівняє його з назвою програми в IIS. Якщо він не відповідає, він видаляє файли cookie, але повідомить браузер ОК. Отже, для цього Identity Server мені потрібно було написати всі URL-адреси в web.config та всі назви додатків у IIS в нижньому регістрі, щоб уникнути подібних проблем.

Також не забудьте дозволити сторонні файли cookie у веб-переглядачі, якщо у вас є додатки поза субдоменом STS, інакше браузер не видалить файли cookie, навіть якщо WIF скаже йому про це.


1
WIF? СТС? Сервер ідентичності ThinkTecture? Що це все за речі, і як вони стосуються цього питання?
Оскар Берггрен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.