Comment empêcher le navigateur d'appeler le popup d'authentification de base et de gérer l'erreur 401 à l'aide de Jquery?

107

Je dois envoyer une demande d'autorisation en utilisant l'authentification de base. J'ai implémenté cela avec succès en utilisant jquery. Cependant, lorsque j'obtiens une erreur 401, la fenêtre contextuelle du navigateur d'authentification de base est ouverte et le rappel d'erreur jquery ajax n'est pas appelé.

Alexey Zakharov
la source

Réponses:

46

J'ai aussi été confronté à ce problème récemment. Étant donné que vous ne pouvez pas modifier le comportement par défaut du navigateur pour afficher la fenêtre contextuelle en cas 401( authentification de base ou digest ), il existe deux façons de résoudre ce problème:

  • Modifiez la réponse du serveur pour ne pas renvoyer un fichier 401. Renvoyez un 200code à la place et gérez-le dans votre client jQuery.
  • Remplacez la méthode que vous utilisez pour l'autorisation par une valeur personnalisée dans votre en-tête. Les navigateurs afficheront la fenêtre contextuelle pour Basic et Digest . Vous devez changer cela à la fois sur le client et sur le serveur.

    headers : {
      "Authorization" : "BasicCustom"
    }

Veuillez également jeter un œil à ceci pour un exemple d'utilisation de jQuery avec l'authentification de base.

nwinkler
la source
10
WWW-Authenticate: xBasic realm = com.example peut le faire, avec le code d'état 401 classique. ce billet de blog m'a montré l'indice (je ne suis pas le propriétaire du blog) loudvchar.blogspot.ca/2010/11
PM
2
@PM, la réponse du blog est une solution parfaite. Notez que si <security:http-basic/>vous utilisez, vous n'avez pas besoin de définir basicAuthenticationFiltermais devez le définir comme <security:http-basic entry-point-ref="myBasicAuthenticationEntryPoint"/>.
Brett Ryan
Pouvez-vous me dire comment remplacer la réponse avant de renvoyer au client, j'utilise jaxrs avec une authentification de base. quelle classe dois-je remplacer pour modifier la réponse?
mohammed sameen
Pour une raison quelconque, je reçois le popup lors du retour d'un 401et WWW-Authenticate:Bearer WWW-Authenticate:NTLM WWW-Authenticate:Negotiatesavez-vous pourquoi ce serait
DevEng
34

Renvoyez un code d'état 400 générique, puis traitez ce côté client.

Ou vous pouvez conserver le 401 et ne pas renvoyer l'en-tête WWW-Authenticate, ce à quoi le navigateur répond avec la fenêtre contextuelle d'authentification. Si l'en-tête WWW-Authenticate est manquant, le navigateur ne demandera pas d'informations d'identification.

Ibraheem
la source
6
@MortenHaraldsen Eh bien, la réponse 401 est la bonne réponse à donner à cette occasion, le problème est que le navigateur gère automatiquement cela de manière native, au lieu de permettre à l'application javascript de le gérer. Vous pouvez vous en tenir à la norme, en ne renvoyant pas la réponse appropriée que la norme recommande, ou vous pouvez choisir de ne pas vous en tenir à la norme, en renvoyant le code de réponse recommandé par la norme. Faites votre choix :)
Ibraheem
Dans mon application express, j'ai corrigé cela avec une seule ligne: res.removeHeader('www-authenticate'); // prevents browser from popping up a basic auth window.
gstroup
1
@Ibraheem, je n'aurais pas pu mieux le dire moi-même. Les normes sont créées par des personnes qui s'assoient et parlent, pas nécessairement celles qui s'assoient et codent.
user2867288
15

Vous pouvez supprimer la fenêtre contextuelle d'authentification de base avec une URL de requête ressemblant à ceci:

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

Si vous obtenez une erreur 401 (nom d'utilisateur ou mot de passe incorrect), elle sera correctement gérée avec le rappel d'erreur jquery. Cela peut causer des problèmes de sécurité (en cas de protocole http au lieu de https), mais cela fonctionne.

UPD: la prise en charge de cette solution sera supprimée dans Chrome 59

Ivan Samovar
la source
INCROYABLE!!!! J'ai résolu mon problème car le problème essayait 192.168.1.1 et le routeur continuait à demander Auth.
Nadav Lebovitch
Si vous allez dans l'onglet "Réseau" sous les outils de développement, dans n'importe quel navigateur, vous pouvez lire le nom d'utilisateur et le mot de passe en texte brut. Cela fonctionne, cependant.
Bruno Finger
19
Ne faites jamais cela s'il vous plaît, les journaux de requêtes sur votre serveur Web me sont beaucoup plus précieux maintenant. Noms d'utilisateur et mots de passe gratuits plus le code de réponse! Merci
Remco
mais comment quelqu'un peut-il empêcher l'
affichage
3
Cette méthode d'authentification est en train de devenir dépréciée et Chrome abandonnera la prise en charge des informations d'identification intégrées, c'est- https://user:pass@host/à- dire dans M59 vers juin 2017. Voir cet article de blog chromestatus pour plus d'informations.
Garywoo
13

Comme d'autres l'ont souligné, la seule façon de modifier le comportement du navigateur est de s'assurer que la réponse ne contient pas de code d'état 401 ou, si c'est le cas, n'inclut pas l'en- WWW-Authenticate: Basictête. Puisque changer le code d'état n'est pas très sémantique et indésirable, une bonne approche consiste à supprimer l'en- WWW-Authenticatetête. Si vous ne pouvez pas ou ne voulez pas modifier votre application de serveur Web, vous pouvez toujours la servir ou la proxy via Apache (si vous n'utilisez pas déjà Apache).

Voici une configuration pour Apache pour réécrire la réponse pour supprimer l'en-tête WWW-Authenticate IFF la requête contient l'en-tête X-Requested-With: XMLHttpRequest(qui est défini par défaut par les principaux frameworks Javascript tels que JQuery / AngularJS, etc ...) ET la réponse contient le en-tête WWW-Authenticate: Basic.

Testé sur Apache 2.4 (je ne sais pas si cela fonctionne avec 2.2). Cela dépend du mod_headersmodule en cours d'installation. (Sur Debian / Ubuntu, sudo a2enmod headerset redémarrez 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
la source
1
Pour faire de même avec Nginx, setproxy_hide_header WWW-Authenticate;
Cuga
7

Utilisez X-Requested-With: XMLHttpRequest avec votre en-tête de demande. L'en-tête de réponse ne contiendra donc pas WWW-Authenticate: Basic.

beforeSend: function (xhr) {
                    xhr.setRequestHeader('Authorization', ("Basic "
                        .concat(btoa(key))));
                    xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
                },
Sedhu
la source
1
Cela n'a eu aucun effet pour moi. Quel type de serveur utilisez-vous qui WWW-Authenticate n'est pas envoyé inclus lorsque vous définissez XMLHttpRequest?
Robert Antonucci
@RobertAntonucci c'est apache tomcat
sedhu
5

Si vous utilisez un serveur IIS, vous pouvez configurer la réécriture d'URL IIS (v2) pour réécrire l'en- WWW-Authenticationtête Nonesur l'URL demandée.

Guidez ici .

La valeur que vous souhaitez modifier est response_www_authenticate.

Si vous avez besoin de plus d'informations, ajoutez un commentaire et je publierai le fichier web.config.

Zymotik
la source
1
Cela a très bien fonctionné. Je voudrais noter que la partie "réponse" doit être écrite comme "RESPONSE_www_authenticate" dans URL Rewrite v2 sur IIS 7.5.
Michael Freeman
3

Si l'en-tête WWW-Authenticate est supprimé, vous n'obtiendrez pas la mise en cache des informations d'identification et ne récupérerez pas l'en-tête d'autorisation dans la demande. Cela signifie que vous devrez maintenant entrer les informations d'identification pour chaque nouvelle demande que vous générez.

user2491441
la source
C'est très important, absolument parfait.
Tez Wingfield
2

Sinon, si vous pouvez personnaliser la réponse de votre serveur, vous pouvez renvoyer un 403 Forbidden.

Le navigateur n'ouvrira pas la fenêtre d'authentification et le rappel jquery sera appelé.

Javier Ferrero
la source
5
Cela va à l'encontre de la spécification HTTP 1.1, où il est dit que "... l'autorisation n'aidera pas et la demande NE DEVRAIT PAS être répétée".
Jukka Dahlbom
1
Il est valide de recevoir 403 tout en étant authentifié pour les ressources auxquelles vous n'êtes pas autorisé à accéder, 401 doit être envoyé là où vous n'avez pas encore été authentifié.
Brett Ryan
1

Dans Safari, vous pouvez utiliser des requêtes synchrones pour éviter que le navigateur n'affiche la fenêtre contextuelle. Bien sûr, les requêtes synchrones ne doivent être utilisées que dans ce cas pour vérifier les informations d'identification de l'utilisateur ... Vous pouvez utiliser une telle requête avant d'envoyer la requête réelle qui peut entraîner une mauvaise expérience utilisateur si le contenu (envoyé ou reçu) est assez lourd.

    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
la source
Dans d'autres contextes, utiliser "OPTIONS" au lieu de "POST" peut également aider.
Emmanuel Sellier
0

Créez une URL / login, puis acceptez les paramètres "utilisateur" et "mot de passe" via GET et ne nécessitent pas d'authentification de base. Ici, utilisez php, node, java, peu importe et analysez votre fichier passwd et faites correspondre les paramètres (utilisateur / pass) avec lui. S'il y a une correspondance, redirigez vers http: // utilisateur: [email protected]/ (cela définira les informations d'identification sur votre navigateur) sinon, envoyez la réponse 401 (sans l'en-tête WWW-Authenticate).

Neiker
la source
Ce serait super pour quiconque essaie de renifler le nom d'utilisateur / mot de passe hors de l'homme en texte brut au milieu des attaques!
Ajax
0

De l'arrière avec Spring Boot, j'ai utilisé BasicAuthenticationEntryPoint personnalisé:

@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
la source