Dans la classe LoginComponent, on peut utiliser directement la classe MessageService pour réaliser l’authentification de l’utilisateur mais ce n’est pas souhaitable car, si l’utilisateur accède directement à une autre page de votre application, cette page (ce composant) ne saura pas si l’utilisateur s’est bien authentifié ou non. Pour pallier ce problème, créez un nouveau service auth, qui conservera cette information dans un de ses attributs, appelons-le is_authenticated. Créez cet attribut. Par dependency injection, une instance de MessageService sera passée au constructeur de la classe AuthService. Dans le constructeur de ce dernier, affichez dans la console un message indiquant qu’une instance de AuthService vient d’être construite.
Rajoutez à votre classe AuthService une méthode sendAuthentication qui, étant
donné une chaîne de caractères login et une chaîne password passées en paramètres,
requête le script checkLogin.php
du backend et renvoie un Observable sur la
réponse du serveur (bien évidemment, vous utiliserez votre service MessageService
pour effectuer cette requête).
Modifiez votre composant LoginComponent afin qu’il n’utilise plus MessageService mais AuthService. Testez bien que votre nouveau composant fonctionne correctement.
On souhaite maintenant modifier le service auth de telle sorte que, quand le backend retourne sa réponse, on puisse mettre à jour l’attribut is_authenticated. Actuellement, votre méthode sendAuthentication doit directement retourner l’observable retourné par sendMessage, autrement dit votre code doit ressembler à :
sendAuthentication(login: string, password: string) : Observable<PhpData> {
return this.message.sendMessage('checkLogin', {
login: login,
password: password
});
}
Comme, pour mettre à jour is_authenticated, il faut attendre la réponse du backend, le plus simple serait de modifier le code ci-dessus de la manière suivante :
1sendAuthentication(login: string, password: string) : Observable<PhpData> {
2 const obs = this.message.sendMessage('checkLogin', {
3 login: login,
4 password: password
5 });
6 obs.subscribe(res => {this.is_authenticated = (res.status == 'ok');});
7 return obs;
8}
Malheureusement, un observable obs n’est censé avoir qu’un seul subscriber. Or, ici, sur la ligne 6 ci-dessus, on a un premier subscriber, et le LoginComponent est un deuxième subscriber puisqu’il doit savoir si le backend a authentifié l’utilisateur ou non avant de passer à la page des cours. Donc cette solution n’est pas terrible.
Une meilleure idée consisterait à créer vous-même un observable. Pour créer « manuellement » un observable, on peut utiliser une expression comme celle ci-dessous :
// on crée un observable qui va transmettre un nombre entier
const obs = new Observable<number>((subscriber) => {
// ici, on peut exécuter n'importe quel code qui permettra de calculer
// la valeur de l'observable.....
// quand on a à disposition les infos que l'observable doit transmettre,
// on les transmet via la méthode next :
subscriber.next(42); // on transmet le nombre 42
});
return obs;
En vous basant sur le code ci-dessus, faites en sorte que :
la méthode sendAuthentication mette à jour l’attribut is_authenticated quand la réponse du serveur arrive;
la méthode sendAuthentication renvoie un observable qui contiendra la réponse du serveur (bien évidemment, on ne requêtera qu’une seule fois le backend).
Actuellement, vous ne pouvez empêcher un utilisateur d’accéder à la page de cours sans s’authentifier. Pour forcer cette authentification, créez une garde :
ng generate guard auth
et indiquez que vous souhaitez que cette garde implémente canActivateChild.
Dans votre fichier app.routes.ts
, faites en sorte que les
routes cours et topics/:id soient enfants d’un path vide, comme le montre
la section « Componentless Routes » tout en bas de la page située à l’url ci-dessous.
https://angular.dev/api/router/Route#componentless-routes
Ce path vide n’est associé à aucun composant, il sert juste à définir qui sont ses enfants, c’est-à-dire les composants impactés par la garde. La manière dont les routes des enfants fonctionnent est assez simple : imaginons que C est enfant de B qui, lui-même est enfant de A, alors la route pour atteindre C est :
route_A/route_B/route_C
Autrement dit, on concatène les routes avec des /. Si topics/:id est enfant du path vide, sa route sera donc /topics/:id.
Testez votre forum. Il devrait fonctionner comme avant (et il n’est toujours pas protégé).
Dans le fichier app.routes.ts
, rajoutez la protection des enfants par la
fonction authGuard de votre garde, comme le montrent les URLs ci-dessous :
https://angular.dev/guide/routing/common-router-tasks#preventing-unauthorized-access
https://angular.io/guide/router-tutorial-toh#canactivatechild-guarding-child-routes
Éditez le fichier auth.guard.ts
et faites en sorte que la fonction authGuard
renvoie false au lieu de true. Maintenant, réessayez de vous logguer et de
naviguer sur votre forum.
Votre site est maintenant protégé, peut-être un peu trop vu que vous ne pouvez plus rien y faire. Pour finaliser votre protection, on veut faire en sorte que la garde renvoie true si vous êtes authentifié et false sinon. Pour cela, il suffit d’utiliser le service AuthService et son attribut is_authenticated. Malheureusement, votre garde n’est pas une classe et on ne peut donc pas passer AuthService par dependency injection dans son constructeur. L’URL ci-dessous vous montre comment faire en utilisant la fonction inject() :
https://angular.dev/guide/di/dependency-injection#injecting-consuming-a-dependency
Maintenant, lorsque votre garde devrait retourner un false, faites en sorte qu’elle fasse un navigateByUrl() vers la route /login. Cela permettra de rediriger l’utilisateur vers la page de login puisqu’il n’est pas encore authentifié.
On va maintenant préparer le déploiement de votre application. C’est-à-dire qu’elle sera servie, non pas via un serveur ng serve local mais via votre serveur web Apache. Normalement, votre application est bien écrite et elle peut être déployée sans modification du code, à une exception près : votre service MessageService encode en « dur » l’URL du backend http://127.0.0.1/....., qui est une URL locale à votre machine et qui rendra donc le backend inaccessible de vos utilisateurs. Il faut donc la modifier quand on déploie l’application, ce qui n’est pas pratique, d’autant que le cycle de vie d’un logiciel est souvent constitué d’une alternance de développements/corrections de bugs et de déploiements.
Pour pallier ce problème, Angular propose la notion d’environnement. On crée un environnement via la commande suivante :
ng generate environments
Cela crée deux fichiers src/environments/environment.ts
et
src/environments/environment.development.ts
. Le premier contient les variables
d’environnement utilisées pour le déploiement tandis que le deuxième contient celles
utilisées quand on exécute un ng serve. L’idée, pour exploiter ces deux fichiers
est simple : ils sont constistués d’un objet environment qui contient
exactement les mêmes propriétés, mais les valeurs de ces dernières sont adaptées
suivant qu’on est en mode déploiement ou en mode développement en local. Quand on
développe, on importe le fichier src/environments/environment.development.ts
.
Quand on déploie l’application, Angular modifie pour vous le nom de ce fichier
et compile donc en important src/environments/environment.ts
. Vous n’avez plus aucune
modification à réaliser quand vous alternez développements et déploiements.
Dans le fichier src/environments/environment.development.ts
, créez une
propriété backendPrefix qui contient le préfixe de l’URL du backend que vous aviez
utilisé dans votre MessageService. Dans ce dernier, remplacez ce préfixe par
backendPrefix.
Dans le fichier src/environments/environment.ts
, créez également la propriété
backendPrefix avec le préfixe permettant de joindre votre backend une fois que
celui-ci sera déployé. Dans l’exercice suivant, on déploiera l’application
(backend + frontend) dans un répertoire forum-angular-php
du serveur Apache.
Le préfixe devrait donc être quelque chose comme :
http://127.0.0.1/forum-angular-php/backend
En revanche, si vous installez votre application sur votre espace à la dirNum, le préfixe devrait être quelque chose comme :
https://votre_identifiant.pedaweb.univ-amu.fr/extranet/forum-angular-php/backend
Actuellement, votre site est en développement (sur le port 4200 de localhost ou 127.0.0.1). Si vous souhaitez le déployer sur votre serveur Apache, vous devrez réaliser les opérations suivantes :
tapez la commande :
ng build --base-href /forum-angular-php/browser/
Le dernier / est important, ne l’oubliez pas.
Si vous obtenez un message vous indiquant que vous avez excédé le budget
maximum, éditez le fichier
angular.json
, faites une recherche sur « budgets » et
augmentez le budget mémoire alloué pour les warnings et les erreurs.
Une fois que la commande fonctionne correctement, celle-ci compile votre application
dans un sous-répertoire appelé dist/frontend/browser
.
Créez un répertoire forum-angular-php
à la racine de votre serveur Apache
et déplacez y le répertoire dist/frontend/browser
créé par le ng build.
Testez votre application. Elle est maintenant accessible via l’url :
http://127.0.0.1/forum-angular-php/browser
Si vous souhaitez qu’elle s’appelle autrement que browser
, il vous
faudra refaire le ng build en indiquant bien après le –base-href
le chemin où se trouvera l’application sur votre serveur Apache.
Regardez les fichiers qui ont été générés, ceux du répertoire
forum-angular-php/browser
. Vous pouvez voir qu’il y a très peu de fichiers.
Angular a en effet compilé tous votre code source.
Le build permet de produire une version optimisée de votre application.
Vous pouvez d’ailleurs utiliser l’application lighthouse
de Chrome ou l’onglet
Performance de l’inspecteur de votre navigateur pour vérifier l’efficacité de
votre application.