기본 인증을 사용하는 경우 웹 사이트에서 사용자를 로그 아웃 할 수 있습니까?
사용자가 인증되면 각 요청에 로그인 정보가 포함되므로 다음 번에 동일한 자격 증명을 사용하여 사이트에 액세스 할 때 사용자가 자동으로 로그인되므로 세션 종료가 충분하지 않습니다.
지금까지 유일한 해결책은 브라우저를 닫는 것이지만 사용성 측면에서는 받아 들일 수 없습니다.
/
페이지 를로드 하면 자동으로 다시 로그인됩니다.
기본 인증을 사용하는 경우 웹 사이트에서 사용자를 로그 아웃 할 수 있습니까?
사용자가 인증되면 각 요청에 로그인 정보가 포함되므로 다음 번에 동일한 자격 증명을 사용하여 사이트에 액세스 할 때 사용자가 자동으로 로그인되므로 세션 종료가 충분하지 않습니다.
지금까지 유일한 해결책은 브라우저를 닫는 것이지만 사용성 측면에서는 받아 들일 수 없습니다.
/
페이지 를로드 하면 자동으로 다시 로그인됩니다.
답변:
기본 인증은 로그 아웃을 관리하도록 설계되지 않았습니다. 당신은 그것을 할 수 있지만 완전히 자동으로 할 수는 없습니다.
사용자가 로그 아웃 링크를 클릭하고 로그인을 요청하는 일반 401과 동일한 영역과 동일한 URL 폴더 레벨을 사용하여 응답으로 '401 Unauthorized'를 보내도록해야합니다.
다음에 잘못된 자격 증명을 입력하도록 지시해야합니다 (예 : 빈 사용자 이름과 비밀번호를 입력하면 "로그 아웃되었습니다"페이지가 나타납니다. 잘못된 / 빈 자격 증명은 이전의 올바른 자격 증명을 덮어 씁니다.
간단히 말해, 로그 아웃 스크립트는 로그인 스크립트의 논리를 반전시키고 사용자 가 올바른 자격 증명을 전달 하지 않은 경우에만 성공 페이지를 반환합니다 .
문제는 다소 호기심 많은 "비밀번호를 입력하지 마십시오"비밀번호 상자가 사용자의 승인을 충족시키는 지 여부입니다. 암호를 자동으로 채우려 고하는 암호 관리자도 여기에 들어갈 수 있습니다.
댓글에 대한 응답으로 추가하려면 편집 : 재 로그인은 약간 다른 문제입니다 (두 단계의 로그 아웃 / 로그인이 분명하지 않은 경우). 재 로그인 링크에 대한 첫 번째 시도는 두 번째 시도 (다른 사용자 이름 / 암호가있을 수 있음)를 수락하는 것보다 먼저 거부해야합니다 (401). 이를 수행 할 수있는 몇 가지 방법이 있습니다. 하나는 로그 아웃 링크에 현재 사용자 이름 (예 : / relogin? username)을 포함하고 자격 증명이 사용자 이름과 일치하면 거부합니다.
bobince의 답변에 추가 ...
Ajax를 사용하면 '로그 아웃'링크 / 버튼을 Javascript 기능에 연결할 수 있습니다. 이 함수가 잘못된 사용자 이름과 비밀번호로 XMLHttpRequest를 보내도록하십시오. 그러면 401이 다시 나타납니다. 그런 다음 document.location을 사전 로그인 페이지로 다시 설정하십시오. 이런 식으로 사용자는 로그 아웃하는 동안 추가 로그인 대화 상자를 볼 수 없으며 잘못된 자격 증명을 입력해야합니다.
사용자에게 https : // log : out@example.com/ 링크를 클릭하도록합니다 . 기존 자격 증명을 유효하지 않은 자격 증명으로 덮어 씁니다. 로그 아웃
당신은 완전히 JavaScript로 할 수 있습니다 :
IE는 기본 인증 캐시를 지우는 표준 API를 오랫동안 가지고 있습니다.
document.execCommand("ClearAuthenticationCache")
작동하면 true를 반환해야합니다. 다른 브라우저에서 false, undefined 또는 blow-up을 반환합니다.
새로운 브라우저 (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, Chrome 44, Opera 31 및 IE 11에서 작동하는 것으로 확인되었습니다.
Bowser 는 브라우저 감지에 사용되며 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
있고 웹킷도 할 수 없어야 $.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) 이제 사용자가 일반 색인 파일로 돌아 왔을 때 사용자 "로그 아웃"을 사용하여 시스템에 자동으로 들어 가려고 시도합니다. 두 번째로 401로 응답하여 파일을 차단하여 로그인 / 암호 대화 상자를 호출해야합니다.
3) 여러 가지 방법으로 로그 아웃 사용자를 허용하는 로그인 백엔드와 그렇지 않은 로그인 백엔드를 생성했습니다. 내 일반 로그인 페이지는 수락하지 않는 로그인 페이지를 사용하고, 내 로그 아웃 페이지는 승인 된 로그인 페이지를 사용합니다.
방금 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"
헤더가 포함 된 경우 쿠키뿐만 아니라 인증 자격 증명도 제거해야합니다. 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 기본 인증 팝업에 자격 증명을 저장하지 않습니다. 다른 브라우저는이를 올바르게 수행합니다.
최신 Chrome 버전에 대한 mthoring의 솔루션을 업데이트했습니다.
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(); ?>
그러나 그것은 당신을 새로운 위치로 리디렉션합니다. 로그 아웃이 없습니다.