Come impedire al browser di richiamare il popup di autenticazione di base e gestire l'errore 401 utilizzando Jquery?


107

Devo inviare la richiesta di autorizzazione utilizzando l'autenticazione di base. L'ho implementato con successo usando jquery. Tuttavia, quando ricevo l'errore 401, il popup del browser di autenticazione di base viene aperto e il callback dell'errore jquery ajax non viene chiamato.


Risposte:


46

Anch'io ho dovuto affrontare questo problema di recente. Poiché non è possibile modificare il comportamento predefinito del browser di mostrare il popup in caso di un'autenticazione 401(di base o digest ), ci sono due modi per risolvere questo problema:

  • Modificare la risposta del server per non restituire un file 401. Restituisci 200invece un codice e gestiscilo nel tuo client jQuery.
  • Cambia il metodo che stai utilizzando per l'autorizzazione con un valore personalizzato nell'intestazione. I browser visualizzeranno il popup per Basic e Digest . Devi modificarlo sia sul client che sul server.

    headers : {
      "Authorization" : "BasicCustom"
    }

Dai un'occhiata anche a questo per un esempio di utilizzo di jQuery con Basic Auth.


10
WWW-Authenticate: xBasic realm = com.example può farlo, insieme al classico codice di stato 401. questo post del blog mi ha mostrato il suggerimento (non sono il proprietario del blog) loudvchar.blogspot.ca/2010/11/…
PM

2
@PM, la risposta del blog è una soluzione perfetta. Si noti che se si utilizza <security:http-basic/>non è necessario definirlo basicAuthenticationFilterma è necessario definirlo come <security:http-basic entry-point-ref="myBasicAuthenticationEntryPoint"/>.
Brett Ryan

Puoi dirmi come ignorare la risposta prima di rispedirla al client, sto usando jaxrs con l'autenticazione di base. quale classe devo sostituire per modificare la risposta?
mohammed sameen

Per qualche motivo ricevo il popup quando restituisco a 401e WWW-Authenticate:Bearer WWW-Authenticate:NTLM WWW-Authenticate:NegotiateSai perché sarebbe
DevEng

34

Restituisce un codice di stato 400 generico, quindi elabora il lato client.

Oppure puoi mantenere il 401 e non restituire l'intestazione WWW-Authenticate, che è effettivamente ciò a cui risponde il browser con il popup di autenticazione. Se manca l'intestazione WWW-Authenticate, il browser non richiederà le credenziali.


6
@MortenHaraldsen Beh, la risposta 401 è la risposta corretta da dare in questa occasione, il problema è che il browser la gestisce automaticamente in modo nativo, invece di consentire all'app javascript di gestirla. Puoi attenersi allo standard, non restituendo la risposta corretta che lo standard consiglia, oppure puoi scegliere di non attenersi allo standard, restituendo il codice di risposta consigliato dallo standard. Fai la tua scelta :)
Ibraheem

Nella mia app express, ho risolto questo problema con una riga: res.removeHeader('www-authenticate'); // prevents browser from popping up a basic auth window.
gstroup

1
@ Ibraheem, non avrei potuto dirlo meglio io stesso. Gli standard sono creati da persone che si siedono e parlano, non necessariamente quelle che si siedono e programmano.
user2867288

15

Puoi sopprimere il popup di autenticazione di base con l'URL della richiesta simile a questo:

https://username:password@example.com/admin/...

Se ricevi un errore 401 (nome utente o password errati), verrà gestito correttamente con la richiamata dell'errore jquery. Può causare alcuni problemi di sicurezza (in caso di protocollo http invece di https), ma funziona.

UPD: il supporto di questa soluzione verrà rimosso in Chrome 59


SORPRENDENTE!!!! Ho risolto il mio problema poiché il problema stava provando con 192.168.1.1 e il router continuava a chiedere l'autenticazione.
Nadav Lebovitch

Se vai alla scheda "Rete" sotto gli Strumenti per sviluppatori, in qualsiasi browser, puoi leggere il nome utente e la password in testo normale. Funziona, però.
Bruno Finger il

19
Non farlo mai per favore, i log delle richieste sul tuo server web sono molto più preziosi per me ora .. Nomi utente e password combinati gratuiti più il codice di risposta! Grazie
Remco

ma come può qualcuno impedire la visualizzazione del nome utente e della password
sdx11

3
Questo metodo di autenticazione sta diventando obsoleto e Chrome interromperà il supporto per le credenziali incorporate, ad esempio https://user:pass@host/in M59, intorno a giugno 2017. Per ulteriori informazioni, vedere questo post del blog di chromestatus .
Garywoo

13

Come altri hanno sottolineato, l'unico modo per modificare il comportamento del browser è assicurarsi che la risposta non contenga un codice di stato 401 o, in caso affermativo, non includere l' WWW-Authenticate: Basicintestazione. Poiché la modifica del codice di stato non è molto semantica e indesiderabile, un buon approccio è rimuovere l' WWW-Authenticateintestazione. Se non puoi o non vuoi modificare la tua applicazione del server web, puoi sempre servirla o proxy tramite Apache (se non stai già usando Apache).

Ecco una configurazione per Apache per riscrivere la risposta per rimuovere l'intestazione WWW-Authenticate IFF che la richiesta contiene contiene l'intestazione X-Requested-With: XMLHttpRequest(che è impostata per impostazione predefinita dai principali framework Javascript come JQuery / AngularJS, ecc ...) E la risposta contiene il intestazione WWW-Authenticate: Basic.

Testato su Apache 2.4 (non sono sicuro che funzioni con 2.2). Questo si basa sul mod_headersmodulo da installare. (Su Debian / Ubuntu sudo a2enmod headerse riavvia 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>   

1
Per fare lo stesso con Nginx, impostaproxy_hide_header WWW-Authenticate;
Cuga

7

Usa X-Requested-With: XMLHttpRequest con l'intestazione della tua richiesta. Quindi l'intestazione della risposta non conterrà WWW-Authenticate: Basic.

beforeSend: function (xhr) {
                    xhr.setRequestHeader('Authorization', ("Basic "
                        .concat(btoa(key))));
                    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
                },

1
Non ha avuto effetto per me. Che tipo di server stai usando che WWW-Authenticate non viene inviato incluso quando imposti XMLHttpRequest?
Robert Antonucci

@RobertAntonucci è apache tomcat
sedhu

5

Se stai utilizzando un server IIS, puoi impostare la riscrittura dell'URL IIS (v2) per riscrivere l' WWW-Authenticationintestazione Nonenell'URL richiesto.

Guida qui .

Il valore che vuoi cambiare è response_www_authenticate.

Se hai bisogno di maggiori informazioni, aggiungi un commento e posterò il file web.config.


1
Ha funzionato alla grande. Vorrei sottolineare che la parte "risposta" deve essere scritta come "RESPONSE_www_authenticate" in URL Rewrite v2 su IIS 7.5.
Michael Freeman

3

Se l'intestazione WWW-Authenticate viene rimossa, non otterrai la memorizzazione nella cache delle credenziali e non riceverai indietro l'intestazione di autorizzazione nella richiesta. Ciò significa che ora dovrai inserire le credenziali per ogni nuova richiesta che generi.


Questo è molto importante, assolutamente perfetto.
Tez Wingfield

2

In alternativa, se puoi personalizzare la risposta del server, potresti restituire un 403 Forbidden.

Il browser non aprirà il popup di autenticazione e verrà chiamato il callback jquery.


5
Ciò va contro la specifica HTTP 1.1, dove si afferma che "... L'autorizzazione non aiuterà e la richiesta NON DOVREBBE essere ripetuta".
Jukka Dahlbom,

1
È valido per ricevere 403 durante l'autenticazione per le risorse a cui non è consentito l'accesso, 401 dovrebbe essere inviato dove non sei ancora stato autenticato.
Brett Ryan

1

In Safari, puoi utilizzare richieste sincrone per evitare che il browser visualizzi il popup. Ovviamente, le richieste sincrone dovrebbero essere utilizzate solo in questo caso per controllare le credenziali dell'utente ... È possibile utilizzare tale richiesta prima di inviare la richiesta effettiva, il che potrebbe causare una cattiva esperienza utente se il contenuto (inviato o ricevuto) è piuttosto pesante.

    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');

In altri contesti, può essere utile anche utilizzare "OPTIONS" invece di "POST".
Emmanuel Sellier,

0

Crea un URL / login, quindi accetta i parametri "utente" e "password" tramite GET e non richiede l'autenticazione di base. Qui, usa php, node, java, qualunque cosa e analizza il tuo file passwd e abbina i parametri (utente / pass) contro di esso. Se c'è una corrispondenza, reindirizza a http: // user: pass@domain.com/ (questo imposterà le credenziali sul tuo browser) in caso contrario, invia la risposta 401 (senza l'intestazione WWW-Authenticate).


Sarebbe fantastico per chiunque cerchi di annusare nome utente / password dagli attacchi man in the middle in testo normale!
Ajax

0

Dal retro con Spring Boot ho usato BasicAuthenticationEntryPoint personalizzato:

@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);
            }
        }
    };
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.