¿Cómo evitar que el navegador invoque la ventana emergente de autenticación básica y maneje el error 401 usando Jquery?

Respuestas:

46

También me enfrenté a este problema recientemente. Dado que no puede cambiar el comportamiento predeterminado del navegador de mostrar la ventana emergente en caso de una 401( autenticación básica o implícita ), hay dos formas de solucionar esto:

  • Cambie la respuesta del servidor para que no devuelva un 401. Devuelve un 200código en su lugar y maneja esto en tu cliente jQuery.
  • Cambie el método que está utilizando para la autorización a un valor personalizado en su encabezado. Los navegadores mostrarán la ventana emergente para Basic y Digest . Tienes que cambiar esto tanto en el cliente como en el servidor.

    headers : {
      "Authorization" : "BasicCustom"
    }
    

Por favor, también eche un vistazo a esto para ver un ejemplo del uso de jQuery con Basic Auth.

nwinkler
fuente
10
WWW-Authenticate: xBasic realm = com.example puede hacerlo, junto con el código de estado 401 clásico. esta publicación de blog me mostró la pista (no soy el propietario del blog) loudvchar.blogspot.ca/2010/11/…
PM
2
@PM, la respuesta del blog es una solución perfecta. Tenga en cuenta que si lo usa <security:http-basic/>, no necesita definirlo, basicAuthenticationFilterpero debe definirlo como <security:http-basic entry-point-ref="myBasicAuthenticationEntryPoint"/>.
Brett Ryan
¿Puede por favor decirme cómo anular la respuesta antes de enviarla al cliente? Estoy usando jaxrs con autenticación básica. ¿Qué clase debo anular para modificar la respuesta?
mohammed sameen
Por alguna razón401WWW-Authenticate:Bearer WWW-Authenticate:NTLM WWW-Authenticate:Negotiate
aparece
34

Devuelve un código de estado 400 genérico y luego procesa ese lado del cliente.

O puede conservar el 401 y no devolver el encabezado WWW-Authenticate, que es realmente a lo que responde el navegador con la ventana emergente de autenticación. Si falta el encabezado WWW-Authenticate, el navegador no solicitará las credenciales.

Ibraheem
fuente
6
@MortenHaraldsen Bueno, la respuesta 401 es la respuesta adecuada para dar en esta ocasión, el problema es que el navegador lo está manejando automáticamente de forma nativa, en lugar de permitir que la aplicación javascript lo maneje. Puede ceñirse al estándar al no devolver la respuesta adecuada que recomienda el estándar, o puede optar por no ceñirse al estándar al devolver el código de respuesta recomendado por el estándar. Elija su opción :)
Ibraheem
En mi aplicación express, arreglé esto con una línea: res.removeHeader('www-authenticate'); // prevents browser from popping up a basic auth window.
gstroup
1
@Ibraheem, no podría haberlo dicho mejor yo mismo. Los estándares son creados por personas que se sientan y hablan, no necesariamente aquellos que se sientan y codifican.
user2867288
15

Puede suprimir la ventana emergente de autenticación básica con la URL de solicitud con este aspecto:

https://username:[email protected]/admin/...

Si recibe un error 401 (nombre de usuario o contraseña incorrectos), se manejará correctamente con la devolución de llamada de error de jquery. Puede causar algunos problemas de seguridad (en el caso del protocolo http en lugar de https), pero funciona.

UPD: el soporte de esta solución se eliminará en Chrome 59

Ivan Samovar
fuente
¡¡¡¡ASOMBROSO!!!! Resolví mi problema ya que el problema estaba intentando 192.168.1.1 y el enrutador seguía solicitando Auth.
Nadav Lebovitch
Si va a la pestaña "Red" debajo de las herramientas del desarrollador, en cualquier navegador, puede leer el nombre de usuario y la contraseña en texto sin formato. Aunque funciona.
Bruno Finger
19
No hagas esto nunca, por favor, los registros de solicitudes en tu servidor web son mucho más valiosos para mí ahora. ¡Combinaciones gratuitas de nombres de usuario y contraseña más el código de respuesta! Gracias
Remco
pero, ¿cómo puede alguien evitar que se
vea
3
Este método de autenticación se está depreciando y Chrome dejará de admitir credenciales integradas, es decir, https://user:pass@host/en M59 alrededor de junio de 2017. Consulte esta publicación de blog de chromestatus para obtener más información.
Garywoo
13

Como han señalado otros, la única forma de cambiar el comportamiento del navegador es asegurarse de que la respuesta no contenga un código de estado 401 o, si lo tiene, no incluya el WWW-Authenticate: Basicencabezado. Dado que cambiar el código de estado no es muy semántico y no es deseable, un buen enfoque es eliminar el WWW-Authenticateencabezado. Si no puede o no desea modificar la aplicación de su servidor web, siempre puede servirla o usarla como proxy a través de Apache (si aún no está usando Apache).

Aquí hay una configuración para que Apache reescriba la respuesta para eliminar el encabezado WWW-Authenticate IFF que la solicitud contiene contiene el encabezado X-Requested-With: XMLHttpRequest(que está configurado de forma predeterminada por los principales marcos de Javascript como JQuery / AngularJS, etc.) Y la respuesta contiene el encabezado WWW-Authenticate: Basic.

Probado en Apache 2.4 (no estoy seguro de si funciona con 2.2). Esto depende del mod_headersmódulo que se está instalando. (En Debian / Ubuntu sudo a2enmod headersy reinicie Apache)

    <Location />
            # Make sure that if it is an XHR request,
            # we don't send back basic authentication header.
            # This is to prevent the browser from displaying a basic auth login dialog.
            Header unset WWW-Authenticate "expr=req('X-Requested-With') == 'XMLHttpRequest' && resp('WWW-Authenticate') =~ /^Basic/"
    </Location>   
Syastrov
fuente
1
Para hacer lo mismo con Nginx, configureproxy_hide_header WWW-Authenticate;
Cuga
7

Utilice X-Requested-With: XMLHttpRequest con el encabezado de su solicitud. Entonces, el encabezado de respuesta no contendrá WWW-Authenticate: Basic.

beforeSend: function (xhr) {
                    xhr.setRequestHeader('Authorization', ("Basic "
                        .concat(btoa(key))));
                    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
                },
sedhu
fuente
1
Eso no tuvo ningún efecto para mí. ¿Qué tipo de servidor está utilizando que WWW-Authenticate no se envía incluido cuando configura XMLHttpRequest?
Robert Antonucci
@RobertAntonucci es apache tomcat
sedhu
5

Si está utilizando un servidor IIS, puede configurar IIS URL Rewriting (v2) para reescribir el WWW-Authenticationencabezado Noneen la URL solicitada.

Guía aquí .

El valor que desea cambiar es response_www_authenticate.

Si necesita más información, agregue un comentario y publicaré el archivo web.config.

Zymotik
fuente
1
Esto funcionó muy bien. Me gustaría señalar que la parte de "respuesta" debe escribirse como "RESPONSE_www_authenticate" en URL Rewrite v2 en IIS 7.5.
Michael Freeman
3

Si se elimina el encabezado WWW-Authenticate, no obtendrá el almacenamiento en caché de las credenciales y no obtendrá el encabezado de autorización en la solicitud. Eso significa que ahora tendrá que ingresar las credenciales para cada nueva solicitud que genere.

usuario2491441
fuente
Esto es muy importante, absolutamente acertado.
Tez Wingfield
2

Alternativamente, si puede personalizar la respuesta de su servidor, puede devolver un 403 Forbidden.

El navegador no abrirá la ventana emergente de autenticación y se llamará a la devolución de llamada de jquery.

Javier Ferrero
fuente
5
Eso va en contra de la especificación HTTP 1.1, donde se establece que "... La autorización no ayudará y la solicitud NO DEBE repetirse".
Jukka Dahlbom
1
Es válido recibir 403 mientras está autenticado para los recursos a los que no tiene permiso de acceso, el 401 debe enviarse donde aún no ha sido autenticado.
Brett Ryan
1

En Safari, puede usar solicitudes sincrónicas para evitar que el navegador muestre la ventana emergente. Por supuesto, las solicitudes síncronas solo deben usarse en este caso para verificar las credenciales del usuario ... Puede usar una solicitud de este tipo antes de enviar la solicitud real, lo que puede causar una mala experiencia de usuario si el contenido (enviado o recibido) es bastante pesado.

    var xmlhttp=new XMLHttpRequest;
    xmlhttp.withCredentials=true;
    xmlhttp.open("POST",<YOUR UR>,false,username,password);
    xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    xmlhttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
Emmanuel Sellier
fuente
En otros contextos, el uso de "OPCIONES" en lugar de "POST" también puede ayudar.
Emmanuel Sellier
0

Cree una URL / login, luego acepte los parámetros de "usuario" y "contraseña" a través de GET y no requiera autenticación básica. Aquí, use php, node, java, lo que sea y analice su archivo passwd y haga coincidir los parámetros (usuario / contraseña) con él. Si hay una coincidencia, redirija a http: // user: [email protected]/ (esto establecerá la credencial en su navegador). De lo contrario, envíe la respuesta 401 (sin el encabezado WWW-Authenticate).

neiker
fuente
¡Eso sería genial para cualquiera que intente olfatear el nombre de usuario / contraseñas de los ataques de texto sin formato man in the middle!
Ajax
0

Desde la parte posterior con Spring Boot, he usado BasicAuthenticationEntryPoint personalizado:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors().and().authorizeRequests()
            ...
            .antMatchers(PUBLIC_AUTH).permitAll()
            .and().httpBasic()
//    https://www.baeldung.com/spring-security-basic-authentication
            .authenticationEntryPoint(authBasicAuthenticationEntryPoint())
            ...

@Bean
public BasicAuthenticationEntryPoint authBasicAuthenticationEntryPoint() {
    return new BasicAuthenticationEntryPoint() {
        {
            setRealmName("pirsApp");
        }

        @Override
        public void commence
                (HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx)
                throws IOException, ServletException {
            if (request.getRequestURI().equals(PUBLIC_AUTH)) {
                response.sendError(HttpStatus.PRECONDITION_FAILED.value(), "Wrong credentials");
            } else {
                super.commence(request, response, authEx);
            }
        }
    };
}
Grigory Kislin
fuente