Archives Wild Code School
Java

Jax-RS

Quête 38 sur 43

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,

xml
<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!

↗
Jax-RS sur Wikipedia
en.wikipedia.org
↗
Configuring Jax-RS applications
docs.oracle.com

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/githubers en 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 POST tel que celui-ci, il convient de retourner la statut 201 (CREATED) et spécifier un header Location. Le plus simple est d'injecter @Context UriInfo puis d'appeler uriInfo.getBaseUriBuilder().path("githuber").path(...).build(). Location indique l'URI à laquelle on peut retourner la resource pour ce Githuber en particulier. Tu peux la construire avec un UriBuilder. 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) ou 410 (GONE) à l'aide de Response.status().

  • Et si on veut le tracker alors qu'il l'est déjà, un 409 (CONFLICT).

Teste le tout avec Postman.

↗
UriInfo
docs.oracle.com
↗
Jax-RS tutorial
docs.oracle.com
↗
Atomput REST API examples
bitworking.org
↗
↗
UriBuilder
docs.oracle.com
↗
HTTP status codes
en.wikipedia.org
↗
Postman
getpostman.com

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.

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 Credentials avec un login et un password

  • et consomme-le très simplement pour retourner un token, comme ci-dessous

java
@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

↗
JWT
jwt.io
↗
JJWT
github.com
↗
Securing Jax-Rs endpoints with JWT
antoniogoncalves.org
↗
Best practice for REST token-based authentication with JAX-RS
github.com
↗
Authentication and Authorization in Jax-RS
dennis-xlc.gitbooks.io
↗
Beginner's guide to JWTs in Java
stormpath.com

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