Jak uniemożliwić przeglądarce wywoływanie podstawowego wyskakującego okienka autoryzacji i obsługę błędu 401 za pomocą JQuery?

107

Muszę wysłać żądanie autoryzacji przy użyciu podstawowego uwierzytelniania. Z powodzeniem zaimplementowałem to przy użyciu jquery. Jednak gdy otrzymuję błąd 401, otwiera się wyskakujące okienko przeglądarki uwierzytelniania podstawowego i nie jest wywoływane wywołanie zwrotne błędu jquery ajax.

Aleksiej Zacharow
źródło

Odpowiedzi:

46

Ostatnio też miałem do czynienia z tym problemem. Ponieważ nie możesz zmienić domyślnego zachowania przeglądarki, polegającego na wyświetlaniu wyskakującego okienka w przypadku uwierzytelnienia 401( podstawowego lub uproszczonego ), istnieją dwa sposoby, aby to naprawić:

  • Zmień odpowiedź serwera, aby nie zwracał pliku 401. 200Zamiast tego zwróć kod i obsłuż go w swoim kliencie jQuery.
  • Zmień metodę, której używasz do autoryzacji, na niestandardową wartość w nagłówku. Przeglądarki wyświetlą wyskakujące okienko Basic i Digest . Musisz to zmienić zarówno na kliencie, jak i na serwerze.

    headers : {
      "Authorization" : "BasicCustom"
    }
    

Proszę również spojrzeć na to, aby zobaczyć przykład użycia jQuery z podstawowym uwierzytelnianiem.

nwinkler
źródło
10
WWW-Authenticate: xBasic realm = com.example może to zrobić, razem z klasycznym kodem statusu 401. ten wpis na blogu pokazał mi wskazówkę (nie jestem właścicielem bloga) loudvchar.blogspot.ca/2010/11/...
PM
2
@PM, odpowiedź bloga jest idealnym rozwiązaniem. Zauważ, że jeśli używasz <security:http-basic/>, nie musisz definiować, basicAuthenticationFilterale powinieneś zdefiniować go jako <security:http-basic entry-point-ref="myBasicAuthenticationEntryPoint"/>.
Brett Ryan,
Czy możesz mi powiedzieć, jak zmienić odpowiedź przed odesłaniem do klienta, używam jaxrs z podstawową autoryzacją. którą klasę muszę zastąpić, aby zmodyfikować odpowiedź?
mohammed sameen
Z jakiegoś powodu otrzymuję wyskakujące okienko po zwróceniu 401i WWW-Authenticate:Bearer WWW-Authenticate:NTLM WWW-Authenticate:NegotiateCzy wiesz, dlaczego tak się
dzieje
34

Zwróć ogólny kod stanu 400, a następnie przetwórz ten kod po stronie klienta.

Możesz też zachować 401 i nie zwracać nagłówka WWW-Authenticate, na który tak naprawdę odpowiada przeglądarka, wyświetlając wyskakujące okienko uwierzytelniania. Jeśli brakuje nagłówka WWW-Authenticate, przeglądarka nie monituje o poświadczenia.

Ibraheem
źródło
6
@MortenHaraldsen Odpowiedź 401 jest właściwą odpowiedzią w tej sytuacji, problem polega na tym, że przeglądarka automatycznie obsługuje to natywnie, zamiast pozwalać aplikacji javascript na obsługę tego. Możesz trzymać się standardu, nie zwracając właściwej odpowiedzi zalecanej przez standard, lub możesz nie trzymać się standardu, zwracając zalecany przez standard kod odpowiedzi. Do wyboru :)
Ibraheem
W mojej aplikacji ekspresowej naprawiłem to za pomocą jednej linii: res.removeHeader('www-authenticate'); // prevents browser from popping up a basic auth window.
gstroup
1
@Ibraheem, sam nie mógłbym tego lepiej określić. Standardy tworzą ludzie, którzy siedzą i rozmawiają, niekoniecznie ci, którzy siedzą i kodują.
user2867288
15

Możesz ukryć wyskakujące okienko autoryzacji z adresem URL żądania wyglądającym tak:

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

Jeśli pojawi się błąd 401 (zła nazwa użytkownika lub hasło), zostanie to poprawnie obsłużone przez wywołanie zwrotne błędu jQuery. Może powodować pewne problemy z bezpieczeństwem (w przypadku protokołu http zamiast https), ale działa.

UPD: ta obsługa rozwiązania zostanie usunięta w Chrome 59

Ivan Samovar
źródło
NIESAMOWITY!!!! Rozwiązałem mój problem, ponieważ problem polegał na wypróbowaniu 192.168.1.1, a router nadal pytał o uwierzytelnianie.
Nadav Lebovitch
Jeśli przejdziesz do zakładki „Sieć” w Narzędziach dla programistów, w dowolnej przeglądarce możesz odczytać nazwę użytkownika i hasło w postaci zwykłego tekstu. Ale działa.
Bruno Finger
19
Nigdy tego nie rób, proszę, dzienniki żądań na twoim serwerze WWW są teraz dla mnie o wiele bardziej wartościowe. Darmowe kombinacje nazw użytkowników i haseł oraz kod odpowiedzi! Dzięki
Remco
ale jak ktoś może uniemożliwić przeglądanie nazwy użytkownika i hasła
sdx11
3
Ta metoda uwierzytelniania staje się przestarzała, a Chrome przestanie obsługiwać osadzone dane logowania, np. https://user:pass@host/W M59 około czerwca 2017 r. Więcej informacji znajdziesz w tym poście na blogu chromestatus .
Garywoo
13

Jak zauważyli inni, jedynym sposobem zmiany zachowania przeglądarki jest upewnienie się, że odpowiedź albo nie zawiera kodu stanu 401, albo jeśli zawiera, nie zawiera WWW-Authenticate: Basicnagłówka. Ponieważ zmiana kodu statusu nie jest zbyt semantyczna i niepożądana, dobrym podejściem jest usunięcie WWW-Authenticatenagłówka. Jeśli nie możesz lub nie chcesz modyfikować aplikacji serwera WWW, zawsze możesz ją obsługiwać lub proxy przez Apache (jeśli nie używasz jeszcze Apache).

Oto konfiguracja dla Apache do przepisania odpowiedzi w celu usunięcia nagłówka IFF WWW-Authenticate, które żądanie zawiera nagłówek X-Requested-With: XMLHttpRequest(który jest domyślnie ustawiany przez główne struktury Javascript, takie jak JQuery / AngularJS itp ...) ORAZ odpowiedź zawiera nagłówek WWW-Authenticate: Basic.

Testowane na Apache 2.4 (nie jestem pewien, czy działa z 2.2). Zależy to od mod_headersinstalowanego modułu. (Na Debianie / Ubuntu sudo a2enmod headersi zrestartuj 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
źródło
1
Aby zrobić to samo z Nginx, ustawproxy_hide_header WWW-Authenticate;
Cuga
7

Użyj X-Requested-With: XMLHttpRequest z nagłówkiem żądania. Więc nagłówek odpowiedzi nie będzie zawierał WWW-Authenticate: Basic.

beforeSend: function (xhr) {
                    xhr.setRequestHeader('Authorization', ("Basic "
                        .concat(btoa(key))));
                    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
                },
sedhu
źródło
1
To nie miało na mnie żadnego efektu. Jakiego typu serwera używasz, gdy uwierzytelnianie WWW nie jest wysyłane podczas ustawiania XMLHttpRequest?
Robert Antonucci
@RobertAntonucci to apache tomcat
sedhu
5

Jeśli używasz serwera IIS, możesz skonfigurować przepisywanie adresów URL usług IIS (v2), aby przepisać WWW-Authenticationnagłówek Nonena żądany adres URL.

Przewodnik tutaj .

Wartość, którą chcesz zmienić, to response_www_authenticate.

Jeśli potrzebujesz więcej informacji, dodaj komentarz, a ja opublikuję plik web.config.

Zymotik
źródło
1
Wyszło świetnie. Chciałbym zauważyć, że część „odpowiedź” musi być zapisana jako „RESPONSE_www_authenticate” w URL Rewrite v2 w IIS 7.5.
Michael Freeman,
3

Jeśli nagłówek WWW-Authenticate zostanie usunięty, nie otrzymasz buforowania poświadczeń i nie odzyskasz nagłówka Authorization w żądaniu. Oznacza to, że teraz będziesz musiał wprowadzić poświadczenia dla każdego nowego żądania, które wygenerujesz.

user2491441
źródło
To bardzo ważne, absolutnie na miejscu.
Tez Wingfield
2

Alternatywnie, jeśli możesz dostosować odpowiedź serwera, możesz zwrócić 403 Forbidden.

Przeglądarka nie otworzy wyskakującego okienka uwierzytelniania i zostanie wywołane wywołanie zwrotne jquery.

Javier Ferrero
źródło
5
Jest to sprzeczne ze specyfikacją HTTP 1.1, w której stwierdza się, że „... autoryzacja nie pomoże i NIE POWINNA powtarzać żądania”.
Jukka Dahlbom
1
Prawidłowe jest otrzymywanie 403 podczas uwierzytelniania zasobów, do których nie masz dostępu, 401 powinno być wysyłane w przypadku, gdy nie zostałeś jeszcze uwierzytelniony.
Brett Ryan,
1

W Safari możesz używać żądań synchronicznych, aby przeglądarka nie wyświetlała wyskakującego okienka. Oczywiście, żądania synchroniczne powinny być używane tylko w tym przypadku do sprawdzania danych uwierzytelniających użytkownika ... Możesz użyć takiego żądania przed wysłaniem rzeczywistego żądania, co może spowodować złe wrażenia użytkownika, jeśli zawartość (wysłana lub odebrana) jest dość ciężka.

    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
źródło
W innych kontekstach może też pomóc użycie „OPCJI” zamiast „POST”.
Emmanuel Sellier
0

Utwórz adres URL logowania /, a następnie zaakceptuj parametry „użytkownik” i „hasło” przez GET i nie wymagaj podstawowej autoryzacji. Tutaj użyj php, node, java, cokolwiek i przeanalizuj swój plik z hasłami i dopasuj do niego parametry (użytkownik / przepustka). Jeśli jest dopasowanie, przekieruj na http: // użytkownik: hasł[email protected]/ (spowoduje to ustawienie poświadczeń w przeglądarce), jeśli nie, wyślij odpowiedź 401 (bez nagłówka WWW-Authenticate).

Neiker
źródło
Byłoby to super-świetne dla każdego, kto próbuje wyłuskać nazwę użytkownika / hasło z ataków zwykłego tekstu w środku ataku!
Ajax
0

Od tyłu ze Spring Boot użyłem niestandardowego BasicAuthenticationEntryPoint:

@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);
            }
        }
    };
}
Grigorij Kislin
źródło