Use autenticación básica con jQuery y Ajax

419

Estoy tratando de crear una autenticación básica a través del navegador, pero realmente no puedo llegar allí.

Si este script no está aquí, la autenticación del navegador se hará cargo, pero quiero decirle al navegador que el usuario está a punto de realizar la autenticación.

La dirección debería ser algo como:

http://username:[email protected]/

Tengo un formulario:

<form name="cookieform" id="login" method="post">
      <input type="text" name="username" id="username" class="text"/>
      <input type="password" name="password" id="password" class="text"/>
      <input type="submit" name="sub" value="Submit" class="page"/>
</form>

Y un guión:

var username = $("input#username").val();
var password = $("input#password").val();

function make_base_auth(user, password) {
  var tok = user + ':' + password;
  var hash = Base64.encode(tok);
  return "Basic " + hash;
}
$.ajax
  ({
    type: "GET",
    url: "index1.php",
    dataType: 'json',
    async: false,
    data: '{"username": "' + username + '", "password" : "' + password + '"}',
    success: function (){
    alert('Thanks for your comment!');
    }
});
Patrioticcow
fuente
66
¿Entonces no quieres que el navegador maneje la autenticación BÁSICA? ¿Por qué no solo usar autenticación basada en formularios?
no.good.at.coding
@ no.good.at.coding Si necesita integrarse con una API de terceros detrás de la autenticación (que es lo que estoy tratando de hacer - developer.zendesk.com/rest_api/docs/core/… )
Brian Hannay

Respuestas:

467

Use la beforeSenddevolución de llamada de jQuery para agregar un encabezado HTTP con la información de autenticación:

beforeSend: function (xhr) {
    xhr.setRequestHeader ("Authorization", "Basic " + btoa(username + ":" + password));
},
ggarber
fuente
66
Estoy usando el ejemplo que usted dio pero no funciona `$ .ajax ({url:" server.in.local / index.php ", beforeSend: function (xhr) {xhr.setRequestHeader (" Autorización "," Básico ”+ EncodeBase64 (“ nombre de usuario: contraseña ”));}, succes: function (val) {// alert (val); alert (" ¡Gracias por tu comentario! ");}}); `
Patrioticcow
69
beforeSend: function(xhr) { xhr.setRequestHeader("Authorization", "Basic " + btoa(username + ":" + password)); };funciona para mí
Rico Suter
12
Problema ... Si las credenciales que paso fallan, en Chrome se le presenta al usuario un cuadro de diálogo para ingresar nuevamente nombre de usuario / pwd. ¿Cómo puedo evitar que este segundo diálogo aparezca si las credenciales fallan?
David
2
¿Esto no dejará el nombre de usuario y la contraseña a la vista de todos? Incluso si está en base64, solo pueden decodificarlo. Lo mismo con la respuesta a continuación.
cbron
66
@calebB La autenticación básica en general solo deja el nombre de usuario y la contraseña a la vista de todos. Esto no es solo un problema con el método descrito aquí. Si se está utilizando autenticación básica, o realmente se está utilizando cualquier autenticación, también se debe emplear SSL.
Jason Jackson
354

Cómo cambian las cosas en un año. Además del atributo de encabezado en lugar de xhr.setRequestHeader, jQuery actual (1.7.2+) incluye un atributo de nombre de usuario y contraseña con la $.ajaxllamada.

$.ajax
({
  type: "GET",
  url: "index1.php",
  dataType: 'json',
  username: username,
  password: password,
  data: '{ "comment" }',
  success: function (){
    alert('Thanks for your comment!'); 
  }
});

EDITAR a partir de comentarios y otras respuestas: Para ser claros, para enviar autenticación de forma preventiva sin una 401 Unauthorizedrespuesta, en lugar de setRequestHeader(pre -1.7) use 'headers':

$.ajax
({
  type: "GET",
  url: "index1.php",
  dataType: 'json',
  headers: {
    "Authorization": "Basic " + btoa(USERNAME + ":" + PASSWORD)
  },
  data: '{ "comment" }',
  success: function (){
    alert('Thanks for your comment!'); 
  }
});
jmanning2k
fuente
15
¿No debería ser usernamey no user? Además, no es exactamente lo mismo: de los docos en línea y mi experiencia parece que no es preventiva como requieren algunas API. En otras palabras, envía el Authorizationencabezado solo cuando se devuelve un código 401.
Stefano Fratini
3
@StefanoFratini: tienes razón en ambos aspectos. Se corrigió el campo de nombre de usuario, y es bueno saber acerca de la autenticación preventiva frente a responder solo en caso de desafío. Establecer el encabezado explícitamente como en otras respuestas permitirá el uso de esta variante 'pasiva' de autenticación básica.
jmanning2k
para mí la opción anterior no funcionó, esto funcionó de maravilla ... gracias
Anoop Isaac
Vea el comentario anterior, corrija esta respuesta para indicar {"Autorización": "Básico" + btoa ("usuario: pasar")} como el encabezado correcto
Jay
2
¡Esta es la respuesta que he estado buscando! La clave en esta respuesta para mí es cómo se usa el 'encabezado' para enviar la autenticación de manera preventiva. Mi pregunta aquí es respondida por esto. stackoverflow.com/questions/28404452/…
Steven Anderson
63

Use la devolución de llamada beforeSend para agregar un encabezado HTTP con la información de autenticación de esta manera:

var username = $("input#username").val();
var password = $("input#password").val();  

function make_base_auth(user, password) {
  var tok = user + ':' + password;
  var hash = btoa(tok);
  return "Basic " + hash;
}
$.ajax
  ({
    type: "GET",
    url: "index1.php",
    dataType: 'json',
    async: false,
    data: '{}',
    beforeSend: function (xhr){ 
        xhr.setRequestHeader('Authorization', make_base_auth(username, password)); 
    },
    success: function (){
        alert('Thanks for your comment!'); 
    }
});
Adrian Toman
fuente
44
debe tener en cuenta que "btoa" utilizado aquí para el cifrado Base64 no es compatible con versiones de IE inferiores a la 10 y en algunas plataformas móviles
SET
1
también, de acuerdo con este desarrollador.mozilla.org/en-US/docs/DOM/window.btoa , debe usar btoa (unescape (encodeURIComponent (str)))
SET
@SET, supongo que debe ser btoa (encodeURIComponent (escape (str)))
Saurabh Kumar
Quería obtener la respuesta, así que me deshice de async false y agregué el parámetro de resultado a la función de éxito. También tenía un encabezado de autorización pregenerado, así que solo lo usé en su lugar.
radtek
1
@SET se debe tener en cuenta que Base64 no es cifrado, es codificación. Si se tratara de cifrado, no sería muy sencillo decodificarlo con una sola llamada de función
Chris
40

O simplemente use la propiedad de encabezados introducida en 1.5:

headers: {"Authorization": "Basic xxxx"}

Referencia: jQuery Ajax API

AsemRadhwi
fuente
¿Podría decirme cómo hacerlo para cada usuario? Me refiero a obtener un token api para cada usuario de la base de datos y enviarlo con un encabezado ajax.
Haniye Shadman
32

Los ejemplos anteriores son un poco confusos, y esta es probablemente la mejor manera:

$.ajaxSetup({
  headers: {
    'Authorization': "Basic " + btoa(USERNAME + ":" + PASSWORD)
  }
});

Tomé lo anterior de una combinación de la respuesta de Rico y Yossi.

La función btoa Base64 codifica una cadena.

Paul Odeon
fuente
55
Esta es una mala idea, ya que enviará la información de inicio de sesión con todas las solicitudes de AJAX, independientemente de la URL. Además, la documentación $.ajaxSetup()dice "Establecer valores predeterminados para futuras solicitudes de Ajax. No se recomienda su uso " .
Zero3
27

Como otros han sugerido, puede configurar el nombre de usuario y la contraseña directamente en la llamada Ajax:

$.ajax({
  username: username,
  password: password,
  // ... other parameters.
});

O use la propiedad de encabezados si prefiere no almacenar sus credenciales en texto sin formato:

$.ajax({
  headers: {"Authorization": "Basic xxxx"},
  // ... other parameters.
});

De cualquier forma que lo envíe, el servidor debe ser muy cortés. Para Apache, su archivo .htaccess debería verse así:

<LimitExcept OPTIONS>
    AuthUserFile /path/to/.htpasswd
    AuthType Basic
    AuthName "Whatever"
    Require valid-user
</LimitExcept>

Header always set Access-Control-Allow-Headers Authorization
Header always set Access-Control-Allow-Credentials true

SetEnvIf Origin "^(.*?)$" origin_is=$0
Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is

Explicación:

Para algunas solicitudes de dominio cruzado, el navegador envía una solicitud de OPCIONES de verificación previa a la que le faltan los encabezados de autenticación. Envuelva sus directivas de autenticación dentro de la etiqueta LimitExcept para responder correctamente a la verificación previa.

Luego, envíe algunos encabezados para indicarle al navegador que tiene permiso para autenticarse y que Access-Control-Allow-Origin otorgue permiso para la solicitud entre sitios.

En algunos casos, el comodín * no funciona como un valor para Access-Control-Allow-Origin: debe devolver el dominio exacto de la persona que llama. Use SetEnvIf para capturar este valor.

TiburónAlley
fuente
55
¡Gracias por la información de que los navegadores envían una solicitud de verificación previa de CORS sin encabezados de autenticación! ¡Detalles como este pueden causar horas de depuración!
jbandi
23

Utilice la función jQuery ajaxSetup , que puede configurar valores predeterminados para todas las solicitudes de ajax.

$.ajaxSetup({
  headers: {
    'Authorization': "Basic XXXXX"
  }
});
Yossi Shasho
fuente
11

JSONP no funciona con autenticación básica, por lo que la devolución de llamada jQuery beforeSend no funcionará con JSONP / Script.

Logré evitar esta limitación agregando el usuario y la contraseña a la solicitud (por ejemplo, usuario: [email protected]). Esto funciona con prácticamente cualquier navegador, excepto Internet Explorer, donde la autenticación a través de URL no es compatible (la llamada simplemente no se ejecutará).

Consulte http://support.microsoft.com/kb/834489 .

arnebert
fuente
supongo que, en cuanto a seguridad, es una buena opción no permitir esto
George Birbilis
El enlace está roto (404).
Peter Mortensen
10

Hay 3 formas de lograr esto como se muestra a continuación

Método 1:

var uName="abc";
var passwrd="pqr";

$.ajax({
    type: '{GET/POST}',
    url: '{urlpath}',
    headers: {
        "Authorization": "Basic " + btoa(uName+":"+passwrd);
    },
    success : function(data) {
      //Success block  
    },
   error: function (xhr,ajaxOptions,throwError){
    //Error block 
  },
});

Método 2:

var uName="abc";
var passwrd="pqr";

$.ajax({
    type: '{GET/POST}',
    url: '{urlpath}',
     beforeSend: function (xhr){ 
        xhr.setRequestHeader('Authorization', "Basic " + btoa(uName+":"+passwrd)); 
    },
    success : function(data) {
      //Success block 
   },
   error: function (xhr,ajaxOptions,throwError){
    //Error block 
  },
});

Método 3:

var uName="abc";
var passwrd="pqr";

$.ajax({
    type: '{GET/POST}',
    url: '{urlpath}',
    username:uName,
    password:passwrd, 
    success : function(data) {
    //Success block  
   },
    error: function (xhr,ajaxOptions,throwError){
    //Error block 
  },
});
GSB
fuente
8

Según la respuesta de SharkAlley, también funciona con nginx.

Estaba buscando una solución para obtener datos mediante jQuery desde un servidor detrás de nginx y restringido por Base Auth. Esto funciona para mi:

server {
    server_name example.com;

    location / {
        if ($request_method = OPTIONS ) {
            add_header Access-Control-Allow-Origin "*";
            add_header Access-Control-Allow-Methods "GET, OPTIONS";
            add_header Access-Control-Allow-Headers "Authorization";

            # Not necessary
            #            add_header Access-Control-Allow-Credentials "true";
            #            add_header Content-Length 0;
            #            add_header Content-Type text/plain;

            return 200;
        }

        auth_basic "Restricted";
        auth_basic_user_file /var/.htpasswd;

        proxy_pass http://127.0.0.1:8100;
    }
}

Y el código JavaScript es:

var auth = btoa('username:password');
$.ajax({
    type: 'GET',
    url: 'http://example.com',
    headers: {
        "Authorization": "Basic " + auth
    },
    success : function(data) {
    },
});

Artículo que me parece útil:

  1. Las respuestas de este tema
  2. http://enable-cors.org/server_nginx.html
  3. http://blog.rogeriopvl.com/archives/nginx-and-the-http-options-method/
Maks
fuente