Как выйти из системы пользователя с веб-сайта с помощью обычной проверки подлинности?


можно ли выйти из пользователя с веб-сайта, если он использует обычную проверку подлинности?

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

пока единственное решение-закрыть браузер, но это неприемлемо с точки зрения удобства использования.

18 230

18 ответов:

обычная проверка подлинности не предназначена для управления выходом из системы. Вы можете сделать это, но не полностью автоматически.

Что вам нужно сделать, это заставить пользователя щелкнуть ссылку выхода и отправить "401 несанкционированный" в ответ, используя ту же область и на том же уровне папки URL, что и обычный 401, который вы отправляете, запрашивая вход.

Они должны быть направлены на ввод неверных учетных данных далее, например. пустое имя пользователя и пароль, и в ответ вы отправляете обратно "у вас есть успешно вышел из системы " страница. Неправильные / пустые учетные данные будут перезаписаны предыдущими правильными учетными данными.

короче говоря, сценарий выхода из системы инвертирует логику сценария входа, возвращая страницу успеха только в том случае, если пользователь не передает данные.

вопрос в том, будет ли несколько любопытное поле пароля "не вводите пароль" соответствовать принятию пользователем. Менеджеры паролей, которые пытаются автоматически заполнить пароль, также могут получить сюда.

изменить, чтобы добавить в ответ на комментарий: повторный вход в систему-это немного другая проблема (если вам не требуется двухэтапный выход/вход, очевидно). Вы должны отклонить (401) первую попытку доступа к ссылке relogin, чем принять вторую (которая предположительно имеет другое имя пользователя/пароль). Есть несколько способов сделать это. Одним из них было бы включить текущее имя пользователя в ссылку выхода из системы (например. - релогин?имя пользователя), и отклонить, когда учетные данные совпадают с именем пользователя.

дополнение к ответу от bobince ...

с Ajax вы можете иметь свою ссылку/кнопку "Выход", подключенную к функции Javascript. Попросите эту функцию Отправить XMLHttpRequest с неверным именем пользователя и паролем. Это должно вернуть 401. Затем установите документ.расположение вернуться на страницу предварительного входа. Таким образом, пользователь никогда не будет видеть дополнительный диалог входа во время выхода из системы, и не нужно помнить, чтобы положить в плохие учетные данные.

пусть пользователь нажмет на ссылку https://log:out@example.com/. это перезапишет существующие учетные данные с недопустимыми; выход из системы.

вы можете сделать это полностью в JavaScript:

IE имеет (в течение длительного времени) стандартный API для очистки кэша базовой аутентификации:

document.execCommand("ClearAuthenticationCache")

должен возвращать true, когда он работает. Возвращает значение false, undefined или взрывается в других браузерах.

новые браузеры (по состоянию на декабрь 2012 года: Chrome, FireFox, Safari) имеют "волшебное" поведение. Если они увидят успешных основной запрос auth с любым фиктивным другим именем пользователя (скажем logout) они ясен кэш учетных данных и, возможно, установить его для этого нового фиктивного имени пользователя, которое необходимо убедиться, что не является допустимым именем пользователя для просмотра содержимого.

основной пример этого:

var p = window.location.protocol + '//'
// current location must return 200 OK for this GET
window.location = window.location.href.replace(p, p + 'logout:password@')

"асинхронный" способ сделать это-сделать вызов AJAX, используя logout имя пользователя. Пример:

(function(safeLocation){
    var outcome, u, m = "You should be logged out now.";
    // IE has a simple solution for it - API:
    try { outcome = document.execCommand("ClearAuthenticationCache") }catch(e){}
    // Other browsers need a larger solution - AJAX call with special user name - 'logout'.
    if (!outcome) {
        // Let's create an xmlhttp object
        outcome = (function(x){
            if (x) {
                // the reason we use "random" value for password is 
                // that browsers cache requests. changing
                // password effectively behaves like cache-busing.
                x.open("HEAD", safeLocation || location.href, true, "logout", (new Date()).getTime().toString())
                x.send("")
                // x.abort()
                return 1 // this is **speculative** "We are done." 
            } else {
                return
            }
        })(window.XMLHttpRequest ? new window.XMLHttpRequest() : ( window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : u ))
    }
    if (!outcome) {
        m = "Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser."
    }
    alert(m)
    // return !!outcome
})(/*if present URI does not return 200 OK for GET, set some other 200 OK location here*/)

вы можете сделать это букмарклет:

javascript:(function(c){var a,b="You should be logged out now.";try{a=document.execCommand("ClearAuthenticationCache")}catch(d){}a||((a=window.XMLHttpRequest?new window.XMLHttpRequest:window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):void 0)?(a.open("HEAD",c||location.href,!0,"logout",(new Date).getTime().toString()),a.send(""),a=1):a=void 0);a||(b="Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser.");alert(b)})(/*pass safeLocation here if you need*/);

подтверждена работа следующей функции для Firefox 40, Chrome 44, Opera 31 и IE 11.
Баузер используется для обнаружения браузера, jQuery также используется.

- secUrl-это url-адрес защищенной паролем области, из которой можно выйти.
- redirUrl-это url-адрес не защищенной паролем области (страница успешного выхода из системы).
- вы можете увеличить таймер перенаправления (в настоящее время 200 мс).

function logout(secUrl, redirUrl) {
    if (bowser.msie) {
        document.execCommand('ClearAuthenticationCache', 'false');
    } else if (bowser.gecko) {
        $.ajax({
            async: false,
            url: secUrl,
            type: 'GET',
            username: 'logout'
        });
    } else if (bowser.webkit) {
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.open("GET", secUrl, true);
        xmlhttp.setRequestHeader("Authorization", "Basic logout");
        xmlhttp.send();
    } else {
        alert("Logging out automatically is unsupported for " + bowser.name
            + "\nYou must close the browser to log out.");
    }
    setTimeout(function () {
        window.location.href = redirUrl;
    }, 200);
}

Это напрямую невозможно с помощью базовой аутентификации.

в спецификации HTTP нет механизма, чтобы сервер сообщал браузеру о прекращении отправки учетных данных, которые уже были представлены пользователем.

существуют" хаки " (см. другие ответы), обычно связанные с использованием XMLHttpRequest для отправки HTTP-запроса с неверными учетными данными для перезаписи первоначально предоставленных.

вот очень простой пример Javascript с использованием jQuery:

function logout(to_url) {
    var out = window.location.href.replace(/:\/\//, '://log:out@');

    jQuery.get(out).error(function() {
        window.location = to_url;
    });
}

этот пользователь журнала выходит, не показывая ему окно входа в браузер снова, а затем перенаправить его на выход страница

Это на самом деле довольно просто.

просто посетите следующее в вашем браузере и используйте неправильные учетные данные: http://username:password@yourdomain.com

Это должно "выйти из системы".

это работает для IE / Netscape / Chrome:

      function ClearAuthentication(LogOffPage) 
  {
     var IsInternetExplorer = false;    

     try
     {
         var agt=navigator.userAgent.toLowerCase();
         if (agt.indexOf("msie") != -1) { IsInternetExplorer = true; }
     }
     catch(e)
     {
         IsInternetExplorer = false;    
     };

     if (IsInternetExplorer) 
     {
        // Logoff Internet Explorer
        document.execCommand("ClearAuthenticationCache");
        window.location = LogOffPage;
     }
     else 
     {
        // Logoff every other browsers
    $.ajax({
         username: 'unknown',
         password: 'WrongPassword',
             url: './cgi-bin/PrimoCgi',
         type: 'GET',
         beforeSend: function(xhr)
                 {
            xhr.setRequestHeader("Authorization", "Basic AAAAAAAAAAAAAAAAAAA=");
         },

                 error: function(err)
                 {
                    window.location = LogOffPage;
             }
    });
     }
  }


  $(document).ready(function () 
  {
      $('#Btn1').click(function () 
      {
         // Call Clear Authentication 
         ClearAuthentication("force_logout.html"); 
      });
  });          
function logout() {
  var userAgent = navigator.userAgent.toLowerCase();

  if (userAgent.indexOf("msie") != -1) {
    document.execCommand("ClearAuthenticationCache", false);
  }

  xhr_objectCarte = null;

  if(window.XMLHttpRequest)
    xhr_object = new XMLHttpRequest();
  else if(window.ActiveXObject)
    xhr_object = new ActiveXObject("Microsoft.XMLHTTP");
  else
    alert ("Your browser doesn't support XMLHTTPREQUEST");

  xhr_object.open ('GET', 'http://yourserver.com/rep/index.php', false, 'username', 'password');
  xhr_object.send ("");
  xhr_object = null;

  document.location = 'http://yourserver.com'; 
  return false;
}
 function logout(url){
    var str = url.replace("http://", "http://" + new Date().getTime() + "@");
    var xmlhttp;
    if (window.XMLHttpRequest) xmlhttp=new XMLHttpRequest();
    else xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
    xmlhttp.onreadystatechange=function()
    {
        if (xmlhttp.readyState==4) location.reload();
    }
    xmlhttp.open("GET",str,true);
    xmlhttp.setRequestHeader("Authorization","Basic xxxxxxxxxx")
    xmlhttp.send();
    return false;
}

все, что вам нужно, это перенаправить пользователя на какой-то URL выхода и вернуть 401 Unauthorized ошибки на нем. На странице ошибки (которая должна быть доступна без базовой аутентификации) вам необходимо предоставить полную ссылку на вашу домашнюю страницу (включая схему и имя хоста). Пользователь нажимает на эту ссылку, и браузер снова запрашивает учетные данные.

пример для Nginx:

location /logout {
    return 401;
}

error_page 401 /errors/401.html;

location /errors {
    auth_basic off;
    ssi        on;
    ssi_types  text/html;
    alias /home/user/errors;
}

страница ошибки /home/user/errors/401.html:

<!DOCTYPE html>
<p>You're not authorised. <a href="<!--# echo var="scheme" -->://<!--# echo var="host" -->/">Login</a>.</p>

основываясь на том, что я прочитал выше, я получил простое решение, которое работает в любом браузере:

1)на странице выхода из системы вы вызываете ajax для вашего входа в систему. Ваш логин бэк-энд должен принять выход пользователя. Как только серверная часть принимает, браузер очищает текущего пользователя и принимает пользователя "выхода из системы".

$.ajax({
    async: false,
    url: 'http://your_login_backend',
    type: 'GET',
    username: 'logout'
});      

setTimeout(function () {
    window.location.href = 'http://normal_index';
}, 200);

2) Теперь, когда пользователь вернулся к обычному индексному файлу, он попытается автоматически войти в систему с пользователем "выход", в этот второй раз вы должны заблокировать его ответьте с помощью 401, чтобы вызвать диалоговое окно логин / пароль.

3) Есть много способов сделать это, я создал два входа в систему, один из которых принимает пользователя выхода из системы, а другой-нет. моя обычная страница входа использует тот, который не принимает, моя страница выхода использует тот, который ее принимает.

  • используйте идентификатор сеанса (cookie)
  • аннулировать идентификатор сеанса на сервере
  • не принимать пользователей с недопустимыми идентификаторами сеанса

этот JavaScript должен работать для всех браузеров последней версии:

//Detect Browser
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
    // Opera 8.0+ (UA detection to detect Blink/v8-powered Opera)
var isFirefox = typeof InstallTrigger !== 'undefined';   // Firefox 1.0+
var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
    // At least Safari 3+: "[object HTMLElementConstructor]"
var isChrome = !!window.chrome && !isOpera;              // Chrome 1+
var isIE = /*@cc_on!@*/false || !!document.documentMode; // At least IE6
var Host = window.location.host;


//Clear Basic Realm Authentication
if(isIE){
//IE
    document.execCommand("ClearAuthenticationCache");
    window.location = '/';
}
else if(isSafari)
{//Safari. but this works mostly on all browser except chrome
    (function(safeLocation){
        var outcome, u, m = "You should be logged out now.";
        // IE has a simple solution for it - API:
        try { outcome = document.execCommand("ClearAuthenticationCache") }catch(e){}
        // Other browsers need a larger solution - AJAX call with special user name - 'logout'.
        if (!outcome) {
            // Let's create an xmlhttp object
            outcome = (function(x){
                if (x) {
                    // the reason we use "random" value for password is 
                    // that browsers cache requests. changing
                    // password effectively behaves like cache-busing.
                    x.open("HEAD", safeLocation || location.href, true, "logout", (new Date()).getTime().toString())
                    x.send("");
                    // x.abort()
                    return 1 // this is **speculative** "We are done." 
                } else {
                    return
                }
            })(window.XMLHttpRequest ? new window.XMLHttpRequest() : ( window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : u )) 
        }
        if (!outcome) {
            m = "Your browser is too old or too weird to support log out functionality. Close all windows and restart the browser."
        }
        alert(m);
        window.location = '/';
        // return !!outcome
    })(/*if present URI does not return 200 OK for GET, set some other 200 OK location here*/)
}
else{
//Firefox,Chrome
    window.location = 'http://log:out@'+Host+'/';
}

добавьте это в свое приложение:

@app.route('/logout')
def logout():
    return ('Logout', 401, {'WWW-Authenticate': 'Basic realm="Login required"'})

я обновил решение mthoring для современных версий Chrome:

function logout(secUrl, redirUrl) {
    if (bowser.msie) {
        document.execCommand('ClearAuthenticationCache', 'false');
    } else if (bowser.gecko) {
        $.ajax({
            async: false,
            url: secUrl,
            type: 'GET',
            username: 'logout'
        });
    } else if (bowser.webkit || bowser.chrome) {
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.open(\"GET\", secUrl, true);
        xmlhttp.setRequestHeader(\"Authorization\", \"Basic logout\");\
        xmlhttp.send();
    } else {
// http://stackoverflow.com/questions/5957822/how-to-clear-basic-authentication-details-in-chrome
        redirUrl = url.replace('http://', 'http://' + new Date().getTime() + '@');
    }
    setTimeout(function () {
        window.location.href = redirUrl;
    }, 200);
}


    function logout(secUrl, redirUrl) {
        if (bowser.msie) {
            document.execCommand('ClearAuthenticationCache', 'false');
        } else if (bowser.gecko) {
            $.ajax({
                async: false,
                url: secUrl,
                type: 'GET',
                username: 'logout'
            });
        } else if (bowser.webkit) {
            var xmlhttp = new XMLHttpRequest();
            xmlhttp.open("GET", secUrl, true);
            xmlhttp.setRequestHeader("Authorization", "Basic logout");
            xmlhttp.send();
        } else {
            alert("Logging out automatically is unsupported for " + bowser.name
                + "\nYou must close the browser to log out.");
        }
        setTimeout(function () {
            window.location.href = redirUrl;
        }, 200);
    }

Я попытался использовать вышеизложенное следующим образом.

?php
    ob_start();
    session_start();
    require_once 'dbconnect.php';

    // if session is not set this will redirect to login page
    if( !isset($_SESSION['user']) ) {
        header("Location: index.php");
        exit;
    }
    // select loggedin users detail
    $res=mysql_query("SELECT * FROM users WHERE userId=".$_SESSION['user']);
    $userRow=mysql_fetch_array($res);
?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Welcome - <?php echo $userRow['userEmail']; ?></title>
<link rel="stylesheet" href="assets/css/bootstrap.min.css" type="text/css"  />
<link rel="stylesheet" href="style.css" type="text/css" />

    <script src="assets/js/bowser.min.js"></script>
<script>
//function logout(secUrl, redirUrl)
//bowser = require('bowser');
function logout(secUrl, redirUrl) {
alert(redirUrl);
    if (bowser.msie) {
        document.execCommand('ClearAuthenticationCache', 'false');
    } else if (bowser.gecko) {
        $.ajax({
            async: false,
            url: secUrl,
            type: 'GET',
            username: 'logout'
        });
    } else if (bowser.webkit) {
        var xmlhttp = new XMLHttpRequest();
        xmlhttp.open("GET", secUrl, true);
        xmlhttp.setRequestHeader("Authorization", "Basic logout");
        xmlhttp.send();
    } else {
        alert("Logging out automatically is unsupported for " + bowser.name
            + "\nYou must close the browser to log out.");
    }
    window.location.assign(redirUrl);
    /*setTimeout(function () {
        window.location.href = redirUrl;
    }, 200);*/
}


function f1()
    {
       alert("f1 called");
       //form validation that recalls the page showing with supplied inputs.    
    }
</script>
</head>
<body>

    <nav class="navbar navbar-default navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="http://www.codingcage.com">Coding Cage</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <ul class="nav navbar-nav">
            <li class="active"><a href="http://www.codingcage.com/2015/01/user-registration-and-login-script-using-php-mysql.html">Back to Article</a></li>
            <li><a href="http://www.codingcage.com/search/label/jQuery">jQuery</a></li>
            <li><a href="http://www.codingcage.com/search/label/PHP">PHP</a></li>
          </ul>
          <ul class="nav navbar-nav navbar-right">

            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
              <span class="glyphicon glyphicon-user"></span>&nbsp;Hi' <?php echo $userRow['userEmail']; ?>&nbsp;<span class="caret"></span></a>
              <ul class="dropdown-menu">
                <li><a href="logout.php?logout"><span class="glyphicon glyphicon-log-out"></span>&nbsp;Sign Out</a></li>
              </ul>
            </li>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav> 

    <div id="wrapper">

    <div class="container">

        <div class="page-header">
        <h3>Coding Cage - Programming Blog</h3>
        </div>

        <div class="row">
        <div class="col-lg-12" id="div_logout">
        <h1 onclick="logout(window.location.href, 'www.espncricinfo.com')">MichaelA1S1! Click here to see log out functionality upon click inside div</h1>
        </div>
        </div>

    </div>

    </div>

    <script src="assets/jquery-1.11.3-jquery.min.js"></script>
    <script src="assets/js/bootstrap.min.js"></script>


</body>
</html>
<?php ob_end_flush(); ?>

но он только перенаправляет вас на новое место. Нет выхода.