基本認証を使用しているユーザーをWebサイトからログアウトすることはできますか?
ユーザーが認証されると、各リクエストにログイン情報が含まれるため、セッションを終了するだけでは不十分です。ユーザーは、次回同じ認証情報を使用してサイトにアクセスしたときに自動的にログインします。
これまでの唯一の解決策はブラウザを閉じることですが、それはユーザビリティの観点から受け入れられません。
/
ページをロードしようとすると、自動的に再度ログインされます。
基本認証を使用しているユーザーをWebサイトからログアウトすることはできますか?
ユーザーが認証されると、各リクエストにログイン情報が含まれるため、セッションを終了するだけでは不十分です。ユーザーは、次回同じ認証情報を使用してサイトにアクセスしたときに自動的にログインします。
これまでの唯一の解決策はブラウザを閉じることですが、それはユーザビリティの観点から受け入れられません。
/
ページをロードしようとすると、自動的に再度ログインされます。
回答:
基本認証は、ログアウトを管理するようには設計されていません。あなたはそれを行うことができますが、完全に自動的ではありません。
あなたがしなければならないことは、ユーザーにログアウトリンクをクリックさせ、「401 Unauthorized」を応答として送信し、ログインを要求する通常の401と同じレルムと同じURLフォルダーレベルで送信することです。
次に、間違った資格情報を入力するように指示する必要があります。空白のユーザー名とパスワードを入力して、「ログアウトに成功しました」ページを返信します。間違った/空白の資格情報は、以前の正しい資格情報を上書きします。
つまり、ログアウトスクリプトはログインスクリプトのロジックを反転させ、ユーザーが適切な認証情報を渡していない場合にのみ成功ページを返します。
問題は、やや好奇心の強い「パスワードを入力しないでください」パスワードボックスがユーザーの同意を満たすかどうかです。パスワードを自動入力しようとするパスワードマネージャもここで邪魔になる可能性があります。
コメントへの応答として追加する編集:再ログインは少し異なる問題です(明らかに2段階のログアウト/ログインが必要な場合を除きます)。最初の再ログインリンクへのアクセスを拒否(401)する必要がありますが、2番目のアクセス(おそらくユーザー名/パスワードが異なる)を受け入れる必要があります。これを行うにはいくつかの方法があります。1つは、ログアウトリンクに現在のユーザー名を含めることです(例:/ relogin?username)。資格情報がユーザー名と一致する場合は拒否します。
ボビンスによる回答への追加...
Ajaxを使用すると、「ログアウト」リンク/ボタンをJavaScript関数に配線できます。この関数に、不正なユーザー名とパスワードを使用してXMLHttpRequestを送信させます。これにより、401が返されます。次に、document.locationをログイン前のページに戻します。このようにして、ユーザーはログアウト中に余分なログインダイアログを表示したり、不正な資格情報を入力したりする必要がなくなります。
ユーザーにhttps:// log:out@example.com/へのリンクをクリックしてもらいます。これにより、既存の資格情報が無効な資格情報で上書きされます。ログアウトします。
あなたはそれを完全にJavaScriptで行うことができます:
IEには、(長い間)基本認証キャッシュをクリアするための標準APIがあります。
document.execCommand("ClearAuthenticationCache")
機能する場合はtrueを返す必要があります。false、未定義のいずれかを返すか、他のブラウザで爆破します。
新しいブラウザ(2012年12月現在:Chrome、FireFox、Safari)は「マジック」の動作をします。偽のその他のユーザー名(としましょう)を使用した基本認証リクエストが成功した場合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@')
上記の「非同期」の方法は、logout
ユーザー名を利用してAJAX呼び出しを行うことです。例:
(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*/);
logout
ユーザー名やログアウトURLの特別なサーバー側処理が必要ですか?
logout
がたまたま存在し、生成されたパスワードを持っている場合です。そのほとんどあり得ないケースでは、ユーザーIDをシステムに存在しないものに変更します。
<a href='javascript:......need*/);'>Logout</a>
以下の機能がFirefoxの40、クロム44、オペラ座31およびIE 11のために働いて確認された
クッパブラウザ検出のために使用され、jQueryのにも使用されます。
-secUrlは、ログアウトするパスワードで保護された領域のURLです。
-redirUrlは、パスワードで保護されていない領域(ログアウト成功ページ)のURLです。
-リダイレクトタイマー(現在は200ms)を増やしたい場合があります。
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);
}
$.ajax
バリアントが同期(async: false
)で、xmlhttp
バリアントが非同期(true
in open()
)である理由はありますか?
(bowser.gecko)
する必要があります(bowser.gecko || bowser.blink)
。
$.ajax
、webkitが使用されるのnew XMLHttpRequest
ですか?gecko / blinkができるべきではなくXMLHttpRequest
、webkitもできるべきではありませ$.ajax
んか?よくわかりません。
jQueryを使用した非常に単純なJavaScriptの例を次に示します。
function logout(to_url) {
var out = window.location.href.replace(/:\/\//, '://log:out@');
jQuery.get(out).error(function() {
window.location = to_url;
});
}
このユーザーをブラウザのログインボックスを再び表示せずにログアウトし、ログアウトしたページにリダイレクトします
これは、基本認証では直接実行できません。
サーバーがブラウザにユーザーがすでに提示した資格情報の送信を停止するように指示するHTTP仕様のメカニズムはありません。
「ハッキング」(他の回答を参照)があり、通常はXMLHttpRequestを使用して、最初に提供されたものを上書きするために不正な資格情報でHTTPリクエストを送信します。
これは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");
});
});
それは実際にはかなり簡単です。
ブラウザで次のページにアクセスして、間違った認証情報を使用してください: http:// username:password@yourdomain.com
「ログアウト」する必要があります。
必要なのは、いくつかのログアウト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>
http_host
に401.html
単に代わりにhost
前者は、ポート番号(非標準ポートが使用されている場合には)追加として、
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;
}
上記で読んだことに基づいて、私はどのブラウザでも機能する簡単な解決策を得ました:
1)ログアウトページで、ログインバックエンドにajaxを呼び出します。ログインバックエンドはログアウトユーザーを受け入れる必要があります。バックエンドが受け入れると、ブラウザは現在のユーザーをクリアし、「ログアウト」ユーザーを想定します。
$.ajax({
async: false,
url: 'http://your_login_backend',
type: 'GET',
username: 'logout'
});
setTimeout(function () {
window.location.href = 'http://normal_index';
}, 200);
2)ユーザーが通常のインデックスファイルに戻ったときに、ユーザー「ログアウト」を使用してシステムに自動的に入力しようとします。2回目は、401で応答してブロックし、ログイン/パスワードダイアログを呼び出す必要があります。
3)これを行うには多くの方法があります。2つのログインバックエンドを作成しました。1つはログアウトユーザーを受け入れ、もう1つは受け入れません。私の通常のログインページは受け入れないものを使用し、私のログアウトページはそれを受け入れるものを使用します。
Chrome(79)、Firefox(71)、およびEdge(44)で以下をテストしたところ、問題なく動作しました。上記の他のスクリプトソリューションを適用します。
「ログアウト」リンクを追加し、クリックすると次のHTMLが返されます
<div>You have been logged out. Redirecting to home...</div>
<script>
var XHR = new XMLHttpRequest();
XHR.open("GET", "/Home/MyProtectedPage", true, "no user", "no password");
XHR.send();
setTimeout(function () {
window.location.href = "/";
}, 3000);
</script>
この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"'})
参考までに、という新しいHTTP応答ヘッダーがありClear-Site-Data
ます。サーバーの応答にClear-Site-Data: "cookies"
ヘッダーが含まれている場合は、認証資格情報(Cookieだけでなく)を削除する必要があります。Chrome 77でテストしましたが、この警告はコンソールに表示されます:
Clear-Site-Data header on 'https://localhost:9443/clear': Cleared data types:
"cookies". Clearing channel IDs and HTTP authentication cache is currently not
supported, as it breaks active network connections.
また、認証資格情報は削除されないため、これは(現時点では)基本的な認証ログアウトの実装には機能しませんが、将来的には機能する可能性があります。他のブラウザではテストしていません。
参照:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Clear-Site-Data
https://www.w3.org/TR/clear-site-data/
https://github.com/w3c/webappsec-clear-site-data
https://caniuse.com/#feat=mdn-http_headers_clear-site-data_cookies
送信https://invalid_login@hostname
はMacのSafariを除くすべての場所で正常に機能します(まあ、Edgeはチェックしていませんが、そこでも機能するはずです)。
ユーザーがHTTP基本認証ポップアップで「パスワードを記憶する」を選択すると、Safariでログアウトが機能しません。この場合、パスワードはキーチェーンアクセスに保存されます([ファインダー]> [アプリケーション]> [ユーティリティ]> [キーチェーンアクセス](またはCMD + SPACEと入力して「キーチェーンアクセス」と入力))。送信https://invalid_login@hostname
してもキーチェーンアクセスには影響しないため、このチェックボックスをオンにすると、MacのSafariからログアウトできなくなります。少なくともそれは私にとってはそれがどのように機能するかです。
MacOS Mojave(10.14.6)、Safari 12.1.2。
以下のコードは、Firefox(73)、Chrome(80)、Safari(12)で正常に動作します。ユーザーがログアウトページに移動すると、コードが実行され、資格情報が削除されます。
//It should return 401, necessary for Safari only
const logoutUrl = 'https://example.com/logout';
const xmlHttp = new XMLHttpRequest();
xmlHttp.open('POST', logoutUrl, true, 'logout');
xmlHttp.send();
また、何らかの理由で、「パスワードを記憶」が選択されている場合でも、SafariはHTTP基本認証ポップアップに資格情報を保存しません。他のブラウザはこれを正しく行います。
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> Hi' <?php echo $userRow['userEmail']; ?> <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="logout.php?logout"><span class="glyphicon glyphicon-log-out"></span> 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(); ?>
しかし、それはあなたを新しい場所にリダイレクトするだけです。ログアウトしません。