web-dev-qa-db-fra.com

Angular2 - Comment gérer au mieux le jeton d'authentification expiré?

J'utilise Angular 2.1.2.

J'ai un jeton d'authentification (utilisant angular2-jwt) et s'il expire, mon appel webApi échoue avec une erreur 401. Je recherche une solution où l'utilisateur ne perdra aucune donnée d'entrée.

Je peux attraper ce 401 et ouvrir un modal avec la connexion. L'utilisateur se connecte ensuite, le modal disparaît et il voit son écran de saisie. Cependant, les demandes ayant échoué affichent des erreurs, je dois donc retraiter les demandes. S'il s'agissait d'un routeur, les données initiales n'ont pas été chargées.

Je pourrais recharger la page, mais si je router.navigate sur la même page, il ne semble pas vraiment recharger la page. Je ne veux pas recharger une page entière sur l'application d'une seule page. Existe-t-il un moyen de forcer router.navigate à s'exécuter, même s'il s'agit de la page actuelle?

La navigation est toujours un problème car je perdrais toutes les nouvelles données d'entrée qui n'ont pas été enregistrées.

Idéalement, la demande serait simplement "suspendue" jusqu'à ce que l'utilisateur se connecte à partir du modal. Je n'ai pas trouvé de moyen de mettre cela en œuvre.

Des idées? Existe-t-il une meilleure pratique?

15
Don Chambers

Habituellement, je fournirais moi-même un HttpService au lieu d'utiliser Http directement. Donc, avec votre exigence, je peux fournir ma propre méthode get() pour enchaîner l'authentification avant d'envoyer de vraies requêtes HTTP.

Voici le service:

@Injectable()
class HttpService {
  constructor(private http: Http, private auth: Authentication) {}

  public get(url: string): Observable<Response> {
    return this.auth.authenticate().flatMap(authenticated => {
      if (authenticated) {
        return this.http.get(url);
      }
      else {
        return Observable.throw('Unable to re-authenticate');
      }
    });
  }
}

Voici le composant pour appeler le service:

@Component({
  selector: 'my-app',
  template: `<h1>Hello {{name}}</h1>
  <button (click)="doSomething()">Do Something</button>

  <div [hidden]="!auth.showModal">
  <p>Do you confirm to log in?</p>
  <button (click)="yes()">Yes</button><button (click)="no()">No</button>
  </div>
  `,
})
export class AppComponent {
   name = 'Angular';

   constructor(private httpSvc: HttpService, public auth: Authentication) {}

   ngOnInit() {
   }

   doSomething() {
     let a = this.httpSvc.get('hello.json').subscribe(() => {
       alert('Data retrieved!');
     }, err => {
       alert(err);
     });
   }

   yes() {
    this.auth.confirm.emit(true);
   }

   no() {
     this.auth.confirm.emit(false);
   }
}

En enchaînant les observables, le service Authentication détermine s'il faut interrompre le flux normal pour afficher le modal (bien qu'il ne vive actuellement qu'avec le composant App, il peut certainement être implémenté séparément). Et une fois qu'une réponse positive est reçue de la boîte de dialogue, le service peut reprendre le flux.

class Authentication {
  public needsAuthentication = true;
  public showModal = false;
  public confirm = new EventEmitter<boolean>();

  public authenticate(): Observable<boolean> {
    // do something to make sure authentication token works correctly
    if (this.needsAuthentication) {
      this.showModal = true;
      return Observable.create(observer => {
        this.confirm.subscribe(r => {
          this.showModal = false;
          this.needsAuthentication = !r; 
          observer.next(r);
          observer.complete();
        });
      });
    }
    else {
      return Observable.of(true);
    }
  }
}

J'ai un exemple complet en direct ici.

http://plnkr.co/edit/C129guNJvri5hbGZGsHp?open=app%2Fapp.component.ts&p=preview

5
yuxhuang

Après avoir effectué une connexion et reçu un jeton (qui, dans mon cas, expire dans 60 minutes), j'ai défini un intervalle qui vérifie chaque minute pour voir si 59 minutes se sont écoulées. Si oui, j'ouvre une boîte de dialogue de connexion.

L'idée, cependant, est que la boîte de dialogue de connexion n'est pas un itinéraire, mais juste une superposition qui s'ouvre au-dessus de l'écran sur lequel l'utilisateur se trouve (ainsi, je mets le composant de connexion dans le code HTML du composant racine de l'application et j'utilise un observable pour appeler la boîte de dialogue de connexion de n'importe où dans l'application).

Lorsque l'utilisateur se reconnecte correctement, je ferme la boîte de dialogue de connexion et l'utilisateur poursuit gaiement son chemin avec l'écran qu'il utilisait avant de se reconnecter.

Je ne sais pas si c'est une "meilleure pratique" mais cela fonctionne dans la situation que vous décrivez. Faites-le moi savoir et je pourrai mettre du code.

3
brando

Idéalement, la demande serait simplement "suspendue" jusqu'à ce que l'utilisateur se connecte à partir du modal. Je n'ai pas trouvé de moyen de mettre cela en œuvre.

Avez-vous essayé de changer votre bouton en utilisant tokenNotExpiredattribute comme dans cet exemple: https://github.com/auth0/angular2-jwt#checking-authentication-to-hideshow-elements-and- poignée-routage

Il vous permet d'empêcher le 401 ...

3
Karbos 538

Utilisez une session de navigateur:

https://developer.mozilla.org/de/docs/Web/API/Window/sessionStorage

Stockez les données sous forme de chaîne json, puis recréez les données du formulaire si la demande échoue.

2
igorzg

Eh bien, le rechargement est simple: (<any>window).location.reload(true);

C'est une bonne idée d'afficher une fenêtre de connexion/mot de passe et de permettre à l'utilisateur de continuer à travailler s'il peut fournir un mot de passe, si l'utilisateur clique sur annuler, redirigez-le vers la page de connexion.

Il existe également des problèmes de connexion, des délais d'attente, des erreurs de serveur. Le problème est qu'il est difficile d'implémenter de manière fiable une gestion correcte en utilisant Angular 2 Http module et rxjs. Personnellement, j'ai abandonné l'utilisation de Http module, car sa gestion des erreurs a une mauvaise conception et a implémenté mon propre module http, qui permet une gestion des erreurs configurable et des tentatives transparentes.

2
kemsky