Jax-RS
Salut intrépide Wilder ! En entreprise, il y a des chances que tu travailles sur un backend Java qui approvisionne un front en Angular / React, une app mobile, ou qui discute avec une autre application. Et le format d'échange par excellence ces temps-ci est le JSON, à travers des interfaces REST.
Heureusement, le JCP a pensé à tout et JEE bénéficie depuis sa version 6, d'une API vraiment très efficace pour répondre à ce besoin.

Objectifs
- Maîtriser l'API Jax-RS
- Mettre en place une gestion des droits avec JWT
Mise en place de l'application
Tiens, justement, une équipe de développeurs ambitieux te propose de créer une application mobile pour suivre tes githubers. Il va falloir lui fournir toutes les fonctionalités que tu offres déjà via ton interface web:
-
Lister les Guthubers
-
Suivre (tracker) un Githuber
-
Ne plus suivre un Githuber
Fais le nécessaire côté dépendances,
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<version>2.1</version>
</dependency>
Puis déclare ton application comme REST et spécifier le chemin spécifique à ces endpoints. /api par exemple. Avec une annotation bien sûr!

Mise en place des endpoints
Comme tu l'as vu dans la quête HTTP, tu peux invoquer une action sur des resources via des URLs couplées à des verbes: GET, POST, PUT, DELETE, etc.
Eventuellement, tu peux aussi rajouter une partie query (?var1=val1&var2=val2...) quand, par exemple, tu veux créer un moteur de recherche, comme celui-ci :
https://www.google.fr/search?q=wildcodeschool.
Ici tes resources sont définies, donc pas besoin de query.
A l'aide d'une nouvelle classe annotée @Path("/") tu vas donc declarer 3 routes avec les verbes appropriés, associés à 3 méthodes:
-
List:
/api/githubersen GET qui retourne une liste de Githubers -
Track:
/api/githuber/{login}en POST -
Untrack:
/api/githuber/{id}en DELETE (ce serait mieux de passer le login mais ça t'obligerait à modifier tous tes DAO)
Un endpoint Jax-RS te permet de retourner soit des objets, qui seront sérialisés en JSON -en précisant éventuellement avec l'annotation @Produces(MediaType.APPLICATION_JSON)- soit un objet javax.ws.rs.core.Response.

Attends, c'est pas tout: normalement, pour bien faire les choses:
-
sur un
POSTtel que celui-ci, il convient de retourner la statut201 (CREATED)et spécifier un headerLocation. Le plus simple est d'injecter@Context UriInfopuis d'appeleruriInfo.getBaseUriBuilder().path("githuber").path(...).build().Locationindique l'URI à laquelle on peut retourner la resource pour ce Githuber en particulier. Tu peux la construire avec unUriBuilder. Cela implique aussi que tu vas devoir faire une 4è méthode qui réponde à cette URI :) -
Sur les autres méthodes, si le Githuber ciblé est introuvable, peut-être convient-il de retourner un status code
404 (NOT FOUND)ou410 (GONE)à l'aide deResponse.status(). -
Et si on veut le tracker alors qu'il l'est déjà, un
409 (CONFLICT).

Teste le tout avec Postman.
Authentification
Tu ne veux pas que ton API soit ouverte aux quatre vents. Il est temps d'implémenter un mécanisme de tokens. La grande modes est aux tokens JWT qui sont consitutés:
-
d'un header donnant le type et l'algo de signature
-
du payload contenant un certain nombre de "claims" (des propriétés portant entre autres l'utilisateur et ses droits)
-
d'un HMAC, une signature cryptographique basée sur les deux premiers éléments, ce qui permettra de vérifier au moment du décodage que le contenu n'a pas été modifié.
Si ta tentative de login est couronée de succès, le serveur t'envoie donc un token JWT, que tu dois ensuite passer à chaque appel de ton API (header "Authentication" : "Bearer <token>"). Tu peux mettre JJWT, une des implémentations Java pour JWT, dans ton pom.xml.
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
Inspire toi grandement de l'exemple de Antonio Goncalvès ou de Posya pour aller vite!
Typiquement:
-
Reprends la classe
JWTTokenNeeded -
Reprends la classe
SimpleKeyGenerator -
Reprends la classe
JWTTokenNeededFilter -
Modifie tes endpoints existants avec l'annotation
JWTTokenNeeded -
Créée un Pojo
Credentialsavec unloginet unpassword -
et consomme-le très simplement pour retourner un token, comme ci-dessous
@Slf4j
@Path("/login")
public class AuthenticatorEndpoint {
@Inject private SimpleKeyGenerator keyGenerator;
@Context private UriInfo uriInfo;
@POST
@Consumes(MediaType.APPLICATION_JSON) //eg: { "login": "wilder", "password": "wcs12345" }
public Response authenticate(Credentials creds) {
log.info("login/password : {}/{}", creds.getLogin(), creds.getPassword());
if(true) { //LET'S ASSUME WE LOG IN HERE
// Issue a token for the user and return it within the response
String token = issueToken(creds.getLogin());
return Response.ok().header(AUTHORIZATION, "Bearer " + token).build();
}
else {
return Response.status(UNAUTHORIZED).build();
}
}
private String issueToken(String login) {
...
}
Teste le tout avec et sans token avec Postman

Challenge
REST, Jax-RS et JWT
Implémente les 4 méthodes de l'API, le endpoint d'authentification, le tout fonctionnant avec JWT.
Critères de validation
- Le endpoint qui liste les Githubers est implémenté
- Le endpoint qui renvoie un Githuber est implémenté
- Le endpoint qui permet de tracker un Githuber est implémenté
- Le endpoint qui permet d'arrêter de tracker un Githuber est implémenté
- Le endpoint du login est implémenté et retourne un token JWT
- Les 4 premiers endpoints ne fonctionnent qu'en passant un token JWT