web-dev-qa-db-fra.com

Angular2 REST demande le code d'état HTTP 401 passe à 0

Je développe une application Angular2. Il semble que lorsque mon jeton d'accès expire, le code d'état HTTP 401 est remplacé par une valeur de 0 dans l'objet Response. Je reçois 401 Unauthorized, mais l'objet ERROR Response a le statut 0. Cela m'empêche de détecter une erreur 401 et de tenter de rafraîchir le jeton. Qu'est-ce qui fait que le code d'état HTTP 401 est changé en code d'état HTTP 0?

Voici une capture d'écran de la console de Firefox:

enter image description here

Voici mon code:

get(url: string, options?: RequestOptionsArgs): Observable<any> 
{
    //console.log('GET REQUEST...', url);

    return super.get(url, options)
        .catch((err: Response): any =>
        {
            console.log('************* ERROR Response', err);

            if (err.status === 400 || err.status === 422)
            {
                return Observable.throw(err);
            }
            //NOT AUTHENTICATED
            else if (err.status === 401)
            {                   
                this.authConfig.DeleteToken();
                return Observable.throw(err);
            }               
            else
            {
                // this.errorService.notifyError(err);
                // return Observable.empty();
                return Observable.throw(err);
            }
        })
        // .retryWhen(error => error.delay(500))
        // .timeout(2000, new Error('delay exceeded'))
        .finally(() =>
        {
            //console.log('After the request...');
        });
}

Ce code réside dans un service http personnalisé qui étend le HTTP d'Angular2 afin que je puisse intercepter les erreurs dans un seul emplacement.

Dans Google Chrome, je reçois ce message d'erreur:

XMLHttpRequest ne peut pas charger https://api.cloudcms.com/repositories/XXXXXXXXXXXXXXXX/branches/XXXXXXXXXXXXXXXX/nodesXXXXXXXXXXXXXXXX . Aucun en-tête "Access-Control-Allow-Origin" n'est présent sur la ressource demandée. L'origine ' http://screwtopmedia.local.solutiaconsulting.com ' n'est donc pas autorisée à y accéder. La réponse avait le code d'état HTTP 401.

C'est déroutant car j'inclus l'en-tête "Access-Control-Allow-Origin" dans la demande.

Voici une image des résultats reçus dans Google Chrome:

enter image description here

J'ai essayé d'accéder à l'en-tête de réponse "WWW-Authenticate" comme moyen d'intercepter 401. Cependant, le code suivant renvoie un NULL:

err.headers.get("WWW-Authenticate")

Il est déroutant que j'obtienne un problème CORS car je ne reçois aucune erreur CORS lorsqu'un jeton d'accès valide est fourni.

Comment intercepter le code d'état HTTP 401? Pourquoi le code d'état HTTP 401 passe-t-il à 0?

Merci de votre aide.

23
Tom Schreck

Je travaille pour Cloud CMS. Je peux confirmer que nous avons correctement défini les en-têtes CORS lors des appels d'API. Cependant, pour 401 réponses, les en-têtes ne sont pas définis correctement. Ce problème a été résolu et le correctif sera disponible sur l'API publique à la fin de cette semaine.

BTW si vous utilisez notre pilote javascript https://github.com/gitana/gitana-javascript-driver au lieu d'écrire directement dans l'API, le flux de nouvelle authentification est géré automatiquement et vous n'avez pas besoin pour intercepter les erreurs 401 du tout.

5
Harry

Le problème est lié aux requêtes CORS , voir ce problème github

Aucun en-tête "Access-Control-Allow-Origin" n'est présent sur la ressource demandée

signifie que 'Access-Control-Allow-Origin' est requis dans les en-têtes de réponse.

Angular n'obtient aucun code d'état, c'est pourquoi il vous donne un 0 qui est dû au fait que le navigateur n'autorise pas xml parser pour analyser le response en raison d'un headers non valide.

Vous devez ajouter des en-têtes CORS corrects à votre error response ainsi que success.

19
Ankit Singh

Si cloudcms.com ne définit pas le Access-Control-Allow-Origin dans la réponse 401, vous ne pouvez rien faire d'autre. Vous devez correctement ouvrir le ticket d'assistance avec eux pour confirmer s'il s'agit d'un comportement normal.

javascript dans les navigateurs (FF, Chrome & Safari) J'ai testé ne recevra aucune information si une erreur CORS se produit, autre que le statut 0. Angular2 n'a aucun contrôle sur celle-ci.


J'ai créé un test simple et j'obtiens le même résultat que le vôtre:

geturl() {
    console.log('geturl() clicked');
    let headers = new Headers({ 'Content-Type': 'application/xhtml+xml' });
    let options = new RequestOptions({ 'headers': headers });
    this.http.get(this.url)
        .catch((error): any => {
            console.log('************* ERROR Response', error);
            let errMsg = (error.message) ? error.message :
                error.status ? `${error.status} - ${error.statusText}` : 'Web Server error';
            return Observable.throw(error);
        })
        .subscribe(
        (i: Response) => { console.log(i); },
        (e: any) => { console.log(e); }
        );
}

Après beaucoup de recherches sur Google, j'ai trouvé ce qui suit ( https://github.com/angular/angular.js/issues/3336 ):

lucassp a commenté le 19 août 2013

J'ai trouvé le problème depuis un moment maintenant mais j'ai oublié de poster ici une réponse. CHAQUE réponse, même l'erreur 500, doit avoir les en-têtes CORS attachés. Si le serveur n'attache pas les en-têtes CORS à la réponse d'erreur 500, alors l'objet XHR ne l'analysera pas, donc l'objet XHR n'aura aucun corps de réponse, état ou toute autre donnée de réponse à l'intérieur.

Le résultat du moniteur réseau Firefox semble prendre en charge la raison ci-dessus.

La demande Javascript recevra une réponse vide. javascript request

Une demande d'URL simple (copiez et collez le lien dans la barre d'adresse) obtiendra le corps de la réponse Plain url request

Javascript utilise XHRHttpRequest pour la communication http.

Lorsqu'une réponse XHR arrive, le navigateur traite l'en-tête, c'est pourquoi vous verrez ces 401 messages réseau. Cependant, si la réponse XHR provient d'un hôte différent, le javascript ET l'en-tête de réponse ne contiennent aucun en-tête CORS (par exemple Access-Control-Allow-Origin: *), le navigateur ne transmettra aucune information à la couche XHR. Par conséquent, le corps de la réponse sera complètement vide sans état (0).

J'ai testé avec FF 48.0.1, Chrome 52.0.2743.116 et Safari 9.1.2, et ils ont tous le même comportement.

Utilisez le moniteur réseau du navigateur pour vérifier l'en-tête de réponse pour ces 401 Unauthorized entrées, il est très probable qu'il n'y ait pas Access-Control-Allow-Origin header et à l'origine du problème auquel vous êtes confronté. Cela peut être un bug ou par conception du côté du fournisseur de services.

Access-Control-Allow-Origin est une réponse uniquement en-tête http. Pour plus d'informations sur https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#The_HTTP_response_headers

6
John Siu

Selon W3C , si l'attribut status est mis à 0, cela signifie que:

  • L'état est NON ENVOYÉ ou OUVERT.

ou

  • L'indicateur d'erreur est défini.

Je pense que ce n'est pas un Angular2 problème, il semble que votre demande ait un problème.

2
Supamiu

Vous devez utiliser un moniteur de protocole http tiers (comme CharlesProxy) plutôt que des outils Chrome dev pour confirmer quels en-têtes sont réellement envoyés au service API et s'il renvoie un 401).

2
Harry

J'ai eu le même problème et était dû au fait que le serveur n'envoyait pas la bonne réponse (même si le journal de la console indiquait un 401 Angular avait le statut 0). Mon serveur était Tomcat avec Java utilisant Spring MVC et Spring Security.

Il fonctionne maintenant en utilisant la configuration suivante:

SecurityConfig.Java

...
protected void configure(HttpSecurity http) throws Exception {
....
    http.exceptionHandling()
            .accessDeniedHandler(accessDeniedHandler)
            .authenticationEntryPoint(authenticationEntryPoint);
    // allow CORS option calls
    http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
 ...

SomeAuthenticationEntryPoint.Java

@Component
public class SomeAuthenticationEntryPoint implements AuthenticationEntryPoint {

  @Override
  public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
    HttpStatus responseStatus = HttpStatus.UNAUTHORIZED;
    response.sendError(responseStatus.value(), responseStatus.getReasonPhrase());
    }
}

web.xml

<error-page>
    <error-code>403</error-code>
    <location>/errors/unauthorised</location>
</error-page>
<error-page>
    <error-code>401</error-code>
    <location>/errors/unauthorised</location>
</error-page>

Contrôleur pour gérer les/erreurs/... précédemment définies

@RestController
@RequestMapping("/errors")
public class SecurityExceptionController {

    @RequestMapping("forbidden")
    public void resourceNotFound() throws Exception {
        // Intentionally empty
    }

    @RequestMapping("unauthorised")
    public void unAuthorised() throws Exception {
        // Intentionally empty
        }

    }
}
1
TimothyBrake

Je suis tombé sur cet article:

Ce qui se passe dans mon cas, c'est que:

  • Je demandais REST de domaine différent
  • La ressource a renvoyé 401, mais aucun "Access-Control-Allow-Origin" n'a été défini dans la réponse d'erreur.
  • Chrome (ou votre navigateur) a reçu 401 et s'affiche dans l'onglet réseau (voir ci-dessous: 401), mais n'est jamais passé à Angular

enter image description here

  • car il n'y avait pas de CORS autorisé (en-tête "Access-Control-Allow-Origin" manquant) - Chrome n'a pas pu transmettre cette réponse dans Angular. De cette façon, je pouvais voir 401 dans le Chrome mais pas Angular.

  • La solution était simple (étend la réponse de TimothyBrake) - pour ajouter l'en-tête manquant dans la réponse d'erreur. Dans Spring Boot, j'ai mis: response.addHeader("Access-Control-Allow-Origin", "*"); et j'ai été trié.

@Component
public class JwtUnauthorizedResponseAuthenticationEntryPoint
  implements AuthenticationEntryPoint {
  @Override
  public void commence(HttpServletRequest request, HttpServletResponse response,
          AuthenticationException authException) throws IOException {

      response.addHeader("Access-Control-Allow-Origin", "*"); 
      response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
              "You would need to provide the Jwt Token to Access This resource");
  }
}

PS: assurez-vous que le bean est fourni dans la configuration HttpSecurity dans votre WebSecurityConfigurerAdapter. Voir: https://www.baeldung.com/spring-security-basic-authentication en cas de doute.

J'espère que cela t'aides.

0
Witold Kaczurba