Como registrar o usuário no site usando autenticação básica?
-
04-07-2019 - |
Pergunta
É possível registrar o usuário de um site se ele estiver usando autenticação básica?
A sessão de matar não é suficiente, uma vez que, uma vez que o usuário é autenticado, cada solicitação contém informações de login; portanto, o usuário está conectado automaticamente na próxima vez que acessar o site usando as mesmas credenciais.
A única solução até agora é fechar o navegador, mas isso não é aceitável do ponto de vista da usabilidade.
Solução
A autenticação básica não foi projetada para gerenciar o registro. Você pode fazer isso, mas não completamente automaticamente.
O que você precisa fazer é fazer com que o usuário clique em um link de logout e enviar um '401 não autorizado' em resposta, usando o mesmo reino e no mesmo nível de pasta URL do 401 normal que você envia solicitando um login.
Eles devem ser direcionados para inserir credenciais erradas a seguir, por exemplo. Uma palavra em branco de nome de usuário e passagem e, em resposta, você envia de volta uma página "Você registrou com sucesso". As credenciais erradas/em branco substituirão as credenciais corretas anteriores.
Em resumo, o script de logout inverte a lógica do script de login, retornando apenas a página de sucesso se o usuário não é passando as credenciais certas.
A questão é se a caixa de senha um tanto curiosa “não digite sua senha” atenderá à aceitação do usuário. Os gerentes de senha que tentam preencher automaticamente a senha também podem atrapalhar aqui.
Editar para adicionar em resposta ao comentário: O re-log-in é um problema ligeiramente diferente (a menos que você precise de um logout/login em duas etapas, obviamente). Você deve rejeitar (401) a primeira tentativa de acessar o link Relogin, do que aceitar o segundo (que presumivelmente possui um nome de usuário/senha diferente). Existem algumas maneiras de fazer isso. Seria incluir o nome de usuário atual no link de logout (por exemplo, /Relogin? Nome de usuário) e rejeitar quando as credenciais correspondem ao nome de usuário.
Outras dicas
Uma adição à resposta de Bobince ...
Com o Ajax, você pode ter seu link/botão 'logout' conectado a uma função JavaScript. Peça a essa função que envie o XmlHttPrequest com um nome de usuário e senha ruim. Isso deve recuperar um 401. Em seguida, defina o documento.Location de volta à página Pré-Login. Dessa forma, o usuário nunca verá a caixa de diálogo de login extra durante o logout, nem deve se lembrar de colocar credenciais ruins.
Peça ao usuário que clique em um link para https: // log: out@example.com/. Isso substituirá as credenciais existentes com as inválidas; registrando -os fora.
Você pode fazer isso inteiramente em JavaScript:
O IE possui (por um longo tempo) API padrão para limpar o cache básico de autenticação:
document.execCommand("ClearAuthenticationCache")
Deve retornar verdadeiro quando funciona. Retorna falsa, indefinida ou explode em outros navegadores.
Novos navegadores (em dezembro de 2012: Chrome, Firefox, Safari) têm comportamento "mágico". Se eles virem um bem sucedido solicitação de autenticação básica com qualquer outro nome de usuário falso (digamos logout
) Eles limpam o cache de credenciais e possivelmente o definem para esse novo nome de usuário falso, que você precisa garantir que não seja um nome de usuário válido para visualizar o conteúdo.
Exemplo básico disso é:
var p = window.location.protocol + '//'
// current location must return 200 OK for this GET
window.location = window.location.href.replace(p, p + 'logout:password@')
Uma maneira "assíncrona" de fazer o exposto é fazer uma chamada de Ajax utilizando o logout
nome de usuário. Exemplo:
(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*/)
Você também pode fazer um Bookmarklet:
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*/);
A seguinte função é confirmada trabalhando para o Firefox 40, Chrome 44, Opera 31 e IE 11.
Bowser é usado para detecção de navegador, o jQuery também é usado.
- Securl é o URL para uma área protegida por senha a partir da qual fazer logon.
- Redirurl é o URL para uma área não protegida por senha (página de sucesso de logout).
- Você pode aumentar o timer de redirecionamento (atualmente 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);
}
Isso não é diretamente possível com a autenticação básica.
Não há mecanismo na especificação HTTP para o servidor dizer ao navegador para parar de enviar as credenciais que o usuário já apresentou.
Existem "hacks" (veja outras respostas) envolvendo o uso do XMLHTTPRequest para enviar uma solicitação HTTP com credenciais incorretas para substituir as originalmente fornecidas.
Aqui está um exemplo JavaScript muito simples usando o jQuery:
function logout(to_url) {
var out = window.location.href.replace(/:\/\//, '://log:out@');
jQuery.get(out).error(function() {
window.location = to_url;
});
}
Este usuário de log fora sem mostrar a ele a caixa de login do navegador novamente e depois redirecioná-lo para um desconectado página
É realmente muito simples.
Basta visitar o seguinte no seu navegador e usar credenciais erradas:http: // nome de usuário: password@yourdomain.com
Isso deve "registrar você".
Isso está funcionando para o 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;
}
Tudo que você precisa é redirecionar o usuário em algum URL de logout e retornar 401 Unauthorized
erro nele. Na página de erro (que deve estar acessível sem autenticação básica), você precisa fornecer um link completo para sua página inicial (incluindo esquema e nome de host). O usuário clicará neste link e o navegador solicitará credenciais novamente.
Exemplo para 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;
}
Página de erro /home/user/errors/401.html
:
<!DOCTYPE html>
<p>You're not authorised. <a href="<!--# echo var="scheme" -->://<!--# echo var="host" -->/">Login</a>.</p>
Este JavaScript deve estar funcionando para todos os navegadores da versão mais recente:
//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+'/';
}
Adicione isso ao seu aplicativo:
@app.route('/logout')
def logout():
return ('Logout', 401, {'WWW-Authenticate': 'Basic realm="Login required"'})
Com base no que li acima, recebi uma solução simples que funciona em qualquer navegador:
1) Na sua página de logout, você chama um Ajax no seu login back -end. Seu login back end deve aceitar o usuário de logout. Depois que o back -end aceitar, o navegador limpa o usuário atual e assume o usuário "logout".
$.ajax({
async: false,
url: 'http://your_login_backend',
type: 'GET',
username: 'logout'
});
setTimeout(function () {
window.location.href = 'http://normal_index';
}, 200);
2) Agora, quando o usuário voltou ao arquivo de índice normal, ele tentará inserir automático no sistema com o usuário "Logout", nesta segunda vez, você deve bloqueá -lo pela resposta com 401 para invocar a caixa de diálogo Login/senha.
3) Existem muitas maneiras de fazer isso, criei dois backins de login, um que aceita o usuário de logout e um que não. Minha página de login normal use a que não aceita, minha página de logout usa a que a aceita.
- Use um ID da sessão (cookie)
- invalidar o ID da sessão no servidor
- Não aceite usuários com IDs de sessão inválidos
Atualizei a solução de Mthoring para versões do Chrome modernas:
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);
}
Eu tentei usar o acima da seguinte maneira.
?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(); ?>
Mas apenas o redireciona para um novo local. Sem logout.
modelo chrome://restart
Na barra de endereços e Chrome, com todos os seus aplicativos em segundo plano, reiniciará e o cache de senha do AUTH será limpo.