Como evitar que o navegador invoque o pop-up de autenticação básico e manipule o erro 401 usando Jquery?

107

Preciso enviar um pedido de autorização usando autenticação básica. Eu implementei isso com sucesso usando jquery. No entanto, quando recebo o erro 401, o pop-up do navegador de autenticação básica é aberto e o retorno de chamada de erro do jquery ajax não é chamado.

Alexey Zakharov
fonte

Respostas:

46

Eu também estava enfrentando esse problema recentemente. Como você não pode alterar o comportamento padrão do navegador de mostrar o pop-up no caso de uma 401( autenticação básica ou digest ), existem duas maneiras de corrigir isso:

  • Altere a resposta do servidor para não retornar a 401. Em 200vez disso, retorne um código e trate-o em seu cliente jQuery.
  • Altere o método que você está usando para autorização para um valor personalizado em seu cabeçalho. Os navegadores exibirão o pop-up para Basic e Digest . Você deve alterar isso no cliente e no servidor.

    headers : {
      "Authorization" : "BasicCustom"
    }

Por favor, também dê uma olhada este para um exemplo de como usar jQuery com Auth Basic.

Nwinkler
fonte
10
WWW-Authenticate: xBasic realm = com.example pode fazer isso, junto com o código de status 401 clássico. esta postagem do blog me mostrou a dica (eu não sou o dono do blog) loudvchar.blogspot.ca/2010/11/…
PM
2
@PM, a resposta do blog é uma solução perfeita. Observe que se estiver usando, <security:http-basic/>você não precisa definir, basicAuthenticationFiltermas deve defini-lo como <security:http-basic entry-point-ref="myBasicAuthenticationEntryPoint"/>.
Brett Ryan
Por favor, você pode me dizer como substituir a resposta antes de enviar de volta para o cliente, estou usando jaxrs com autenticação básica. qual classe eu preciso substituir para modificar a resposta?
Mohammed Sameen
Por algum motivo, estou recebendo o pop-up ao retornar um 401e WWW-Authenticate:Bearer WWW-Authenticate:NTLM WWW-Authenticate:NegotiateVocê sabe por que seria
DevEng
34

Retorne um código de status 400 genérico e, em seguida, processe esse lado do cliente.

Ou você pode manter o 401 e não retornar o cabeçalho WWW-Authenticate, que é realmente o que o navegador está respondendo com o pop-up de autenticação. Se o cabeçalho WWW-Authenticate estiver ausente, o navegador não solicitará as credenciais.

Ibraheem
fonte
6
@MortenHaraldsen Bem, a resposta 401 é a resposta adequada a dar nesta ocasião, o problema é que o navegador está lidando com isso automaticamente de forma nativa, em vez de permitir que o aplicativo javascript faça isso. Você pode manter o padrão, não retornando a resposta adequada que o padrão recomenda, ou pode escolher não manter o padrão, retornando o código de resposta recomendado pelo padrão. Faça sua escolha :)
Ibraheem
Em meu aplicativo expresso, res.removeHeader('www-authenticate'); // prevents browser from popping up a basic auth window.
corrigi
1
@Ibraheem, eu não poderia ter dito melhor. Os padrões são criados por pessoas que sentam e conversam, não necessariamente aquelas que sentam e codificam.
user2867288 01 de
15

Você pode suprimir o pop-up de autenticação básico com url de solicitação semelhante a este:

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

Se você obtiver o erro 401 (nome de usuário ou senha incorretos), ele será tratado corretamente com o retorno de chamada de erro do jquery. Isso pode causar alguns problemas de segurança (no caso do protocolo http em vez de https), mas funciona.

UPD: o suporte desta solução será removido no Chrome 59

Ivan Samovar
fonte
SURPREENDENTE!!!! Resolvi meu problema porque o problema estava tentando 192.168.1.1 e o roteador pedia o Auth.
Nadav Lebovitch
Se você acessar a guia "Rede" nas ferramentas do desenvolvedor, em qualquer navegador, poderá ler o nome de usuário e a senha em texto simples. Mas funciona.
Bruno Finger
19
Nunca faça isso, os logs de solicitações no seu servidor web são muito mais valiosos para mim agora. Combinações gratuitas de nomes de usuário e senha mais o código de resposta! Obrigado
Remco
mas como alguém pode impedir que o nome de usuário e a senha sejam vistos
sdx11
3
Este método de autenticação está se tornando obsoleto e o Chrome deixará de oferecer suporte para credenciais incorporadas, ou seja, https://user:pass@host/no M59 por volta de junho de 2017. Veja esta postagem do blog chromestatus para mais informações.
Garywoo
13

Como outros apontaram, a única maneira de alterar o comportamento do navegador é garantir que a resposta não contenha um código de status 401 ou, se contiver, não inclua o WWW-Authenticate: Basiccabeçalho. Como alterar o código de status não é muito semântico e indesejável, uma boa abordagem é remover o WWW-Authenticatecabeçalho. Se você não pode ou não deseja modificar seu aplicativo de servidor web, você sempre pode servir ou proxy através do Apache (se você ainda não estiver usando o Apache).

Aqui está uma configuração para o Apache reescrever a resposta para remover o cabeçalho WWW-Authenticate IFF que a solicitação contém contém o cabeçalho X-Requested-With: XMLHttpRequest(que é definido por padrão pelas principais estruturas Javascript, como JQuery / AngularJS, etc ...) E a resposta contém o cabeçalho WWW-Authenticate: Basic.

Testado em Apache 2.4 (não tenho certeza se funciona com 2.2). Isso depende do mod_headersmódulo que está sendo instalado. (No Debian / Ubuntu, sudo a2enmod headerse reinicie o 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
fonte
1
Para fazer o mesmo com o Nginx, definaproxy_hide_header WWW-Authenticate;
Cuga
7

Use X-Requested-With: XMLHttpRequest com seu cabeçalho de solicitação. Portanto, o cabeçalho da resposta não conterá WWW-Authenticate: Basic.

beforeSend: function (xhr) {
                    xhr.setRequestHeader('Authorization', ("Basic "
                        .concat(btoa(key))));
                    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
                },
Sedhu
fonte
1
Isso não teve nenhum efeito para mim. Que tipo de servidor você está usando que o WWW-Authenticate não é enviado incluído quando você define XMLHttpRequest?
Robert Antonucci,
@RobertAntonucci é apache tomcat
sedhu
5

Se estiver usando um servidor IIS, você pode configurar a regravação de URL do IIS (v2) para reescrever o WWW-Authenticationcabeçalho Noneno URL solicitado.

Guia aqui .

O valor que você deseja alterar é response_www_authenticate.

Se precisar de mais informações, adicione um comentário e postarei o arquivo web.config.

Zymotik
fonte
1
Isso funcionou muito bem. Gostaria de observar que a parte "resposta" deve ser escrita como "RESPONSE_www_authenticate" em URL Rewrite v2 no IIS 7.5.
Michael Freeman de
3

Se o cabeçalho WWW-Authenticate for removido, você não obterá o cache de credenciais e não receberá de volta o cabeçalho Authorization na solicitação. Isso significa que agora você terá que inserir as credenciais para cada nova solicitação gerada.

user2491441
fonte
Isso é muito importante, absolutamente correto.
Tez Wingfield de
2

Como alternativa, se você pode personalizar a resposta do servidor, você pode retornar um 403 Forbidden.

O navegador não abrirá o pop-up de autenticação e o retorno de chamada do jquery será chamado.

Javier Ferrero
fonte
5
Isso vai contra a especificação HTTP 1.1, onde se afirma que "... a autorização não ajudará e a solicitação NÃO DEVE ser repetida".
Jukka Dahlbom
1
É válido receber 403 enquanto é autenticado para recursos aos quais você não tem permissão de acesso, 401 deve ser enviado para onde você ainda não foi autenticado.
Brett Ryan
1

No Safari, você pode usar solicitações síncronas para evitar que o navegador exiba o pop-up. Obviamente, as solicitações síncronas só devem ser usadas neste caso para verificar as credenciais do usuário ... Você pode usar essa solicitação antes de enviar a solicitação real, o que pode causar uma experiência ruim para o usuário se o conteúdo (enviado ou recebido) for muito 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
fonte
Em outros contextos, usar "OPÇÕES" em vez de "POST" também pode ajudar.
Emmanuel Sellier
0

Faça um / login url, aceite os parâmetros "usuário" e "senha" via GET e não exija autenticação básica. Aqui, use php, node, java, qualquer coisa e analise seu arquivo passwd e combine os parâmetros (user / pass) com ele. Se houver uma correspondência, redirecione para http: // user: [email protected]/ (isso definirá a credencial em seu navegador); caso contrário, envie uma resposta 401 (sem o cabeçalho WWW-Authenticate).

Neiker
fonte
Isso seria ótimo para qualquer pessoa tentando detectar nomes de usuário / senhas de ataques de man in the middle-text!
Ajax de
0

No verso do Spring Boot, usei 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
fonte