1. Json Web Token with Spring boot
Introduction
Dans ce didacticiel, vous apprendrez à implanter l'authentification Json Web Token
( JWT ) à l'aide de Spring Boot et Spring Security. Tout d'abord, vous passerez en
revue une théorie de base concernant les JWT, puis vous réaliserez des cas pratiques
et les implanterez dans votre application Spring.
JSON Web Token (JWT) est une norme Internet ouverte pour le partage
d'informations sécurisées entre deux parties. Le jeton contient une « charge utile »
JSON qui est signé numériquement (avec un code privé ou une clé publique/privée) à
l'aide d'un algorithme cryptographique. La signature numérique protège le jeton
contre la falsification.
Un JWT ressemble à ceci :
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibm
FtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.FGK5PCL49k3jfNCq6wZt
n6T-uG9Dv4huxOY8Im55xTuxOYw
Un morceau de texte assez intimidant,
Si vous regardez attentivement, vous remarquerez deux symboles point (.) dans le
JWT. Ces symboles de période divisent le JWT en trois segments : en-tête, charge
utile et signature.
La forme générale d'un JWT est
-> header.payload.signature.
Formateur Jaouad Assabbour Avril 2022
2. Header
La première partie du jeton,l'en-tête est un objet JSON contenant deux propriétés,
type(représente le type du jeton qui est JWT) et alg(l'algorithme à utiliser pour la
signature).
{
"type": "JWT",
"alg": "HS256"
}
Cet objet JSON est encodé en Base64Url pour former la première partie de la chaîne.
Charge utile
Payload
La deuxième partie du jeton, la charge utile contient les données, ou "claims", que
vous souhaitez transférer à l'aide de ce JWT. Il existe certaines revendications
définies telles que
sub — Objet du jeton
iss — Émetteur du jeton
exp — Heure d'expiration du jeton
aud — Public du jeton
Vous pouvez également ajouter des revendications personnalisées sur lesquelles les
deux extrémités se sont mises d'accord et fournir des informations supplémentaires
sur le jeton. Dans l'exemple ci-dessous, "rôle" est une revendication personnalisée.
{
"sub": "john.doe@gmail.com",
"iss": "ABC Pvt Ltd",
"rôle": "ADMINISTRATEUR"
}
Formateur Jaouad Assabbour Avril 2022
3. Signature
La signature est crée en prenant les chaînes codées des deux
premières parties et en les transmettant à l'algorithme de signature
avec votre secret.
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret(signature)
)
Formateur Jaouad Assabbour Avril 2022
4. Partie Sprning BOOT
Rappel création d’un projet
Pour créer un nouveau projet Spring Boot :
• Vous pouvez générer un projet Spring Boot facilement et rapidement à l’aide
d’un formulaire via une interface web ergonomique sur http://start.spring.io.
• Vous pouvez également vous rendre sur Eclipse STS, faire un « New »,
« Spring Starter Project ».
Dans les 2 cas, vous devrez configurez les informations de l’artifact, donnez un nom
à votre projet et choisir les starters correspondant aux fonctionnalités que vous voulez
mettre en place dans votre projet. Les autres paramètres peuvent être laissez par
défaut.
Architecture d’un projet
- /src
- /main
- /java : contenant tous les fichiers java de votre projet
- /ressources
- /static : contenant les fichiers web statiques (css, js...)
- /templates : contenant les vues (html, php, jsp...)
- application.properties : fichier de configuration de votre application
- /test
- /java : contenant vos classes de tests (unitaire, intégration...)
- /target : contenant les différents fichiers générés après un build maven
- pom.xml : fichier de configuration Maven de votre projet
Lancement d’un projet
Pour tester votre application, il suffit de lancer un simple « Run as Java Application »
sur le fichier contenant la méthode main de votre projet.
Il est essentiel pour commencer la partie JSON WEB TOKEN
que la partie spring boot est bien acquis. Ainsi, nous pourrons
nous intéresser directement aux éléments JWT.
Formateur Jaouad Assabbour Avril 2022
5. Partie Pom.XML (dependency)
Spring Security JWT est une bibliothèque utilitaire pour encoder
et décoder les jetons Web JSON.pour l'injecter a mon projets , je
dois ajouter dans mon fichier pom.XML ce code dessus
<!--
https://mvnrepository.com/artifact/org.springframework.security/s
pring-security-jwt →
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-jwt</artifactId>
<version>1.1.1.RELEASE</version>
</dependency>
Formateur Jaouad Assabbour Avril 2022
6. Créons nos Entités
une fois que nous avons créé le projet, nous pouvons y créer nos entités MyUser et
MyRole , (! MyUser, pour pas confendu avec Le User de spring)
Ex creation entity MyUser :
Formateur Jaouad Assabbour Avril 2022
8. DAO ( Data Access Object )
une fois nos entités sont créés, nous allons créer la coche de DAO
MyUser Repository , avec une méthode find User byUserName
MyRole Repository , avec une methode find User byRoleName
Formateur Jaouad Assabbour Avril 2022
9. Register nouvel utilisateur
Pour obtenir un token on a besoin d'un premier temps d'enregistrer les coordonnées ,
afin de les comparer avec les identifiants que le utilisateur à fornerau moment de sa
connection à notre appliquation.
Et pour cela on va créer un package qu'on va appeler services
Couche Service,
On va créer une interface avec quatre méthodes, qui nous permettrons d’enregistrer
un nouvel utilisateur, un rôle, et lire un utilisateur avec son nom, et une qui nous
permettrons d’ajouter un rôle à un utilisateur
Formateur Jaouad Assabbour Avril 2022
10. Puis on va créer une interface qui va implanter cette interface qu'on re définir les
méthodes de l’interface mère.
suite ...
Formateur Jaouad Assabbour Avril 2022
11. Couche Web
Puis je vais créer un controller avec une méthode register, qui va appeler ma méthode
save user de l'interface account service
Formateur Jaouad Assabbour Avril 2022
12. suite,je vais créer une classe UserForm
Couche sécurité
login pour générer de token l'architecture de sécurité Spring
Avant de commencer à personnaliser la configuration, discutons d'abord du
fonctionnement de l'authentification Spring Security dans les coulisses.
Le diagramme suivant présente le flux et montre comment les demandes
d'authentification sont traitées :
Formateur Jaouad Assabbour Avril 2022
13. c’est parti
Authentification à l'aide de JWT avec Spring Security
Pour personnaliser Spring Security, nous avons besoin d'une classe de configuration
annotée avec l'annotation @EnableWebSecurity dans notre chemin de classe. De
plus, pour simplifier le processus de personnalisation, le framework expose une
classe WebSecurityConfigurerAdapter. Nous allons étendre cet adaptateur et
remplacer ses deux fonctions afin de :
=> Configurer le gestionnaire d'authentification avec le bon fournisseur
=> Configurer la sécurité Web (URL publiques, URL privées, autorisation, etc.)
Formateur Jaouad Assabbour Avril 2022
14. Maintenant, lorsque nous acceptons la demande d'authentification, nous devons
récupérer l'identité correcte de la base de données à l'aide des informations
d'identification fournies, puis la vérifier. Pour cela, nous avons besoin de
l'implémentation de l'interface UserDetailsService, qui est définie comme suit :
En plus du fournisseur d'authentification, nous devons configurer un gestionnaire
d'authentification avec le schéma de codage de mot de passe correct qui sera utilisé
Formateur Jaouad Assabbour Avril 2022
15. pour la vérification des informations d'identification. Pour cela, nous devons exposer
l'implémentation préférée de l'interface PasswordEncoder en tant que bean.
BcryptPasswordEncoder
au démarrage de mon application je vais créer une méthode qui retourne l' instancier
de BcryptPasswordEncoder()
@Bean
BCryptPasswordEncoder getBCPE() {
return new BCryptPasswordEncoder();
}
User detail service
nous avons besoin de UserDetailsService pour obtenir l'objet UserDetails. Vous
pouvez consulter l'interface UserDetailsService qui n'a qu'une seule méthode :
UserDetails loadUserByUsername(String username) throws
UsernameNotFoundException;
Formateur Jaouad Assabbour Avril 2022
16. Configure web security
@Override
protected void configure(HttpSecurity http) throws
Exception {
}
csrf
À partir de Spring Security 4.x, la protection CSRF est
activée par défaut. Cette configuration par défaut ajoute
le jeton CSRF à l'attribut HttpServletRequest nommé
_csrf. Si nous en avons besoin, nous pouvons désactiver
cette configuration:
@Override
protected void configure(HttpSecurity http) throws
Exception {
http
.csrf().disable();
}
exemple:
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}"/>
http.sessionManagement().sessionCreationPolicy();
Enfin, l'option de création de session la plus stricte, « sans état », est une garantie
que l'application ne créera aucune session.
Ces mécanismes de contrôle plus stricts impliquent directement que les cookies ne
sont pas utilisés, et donc chaque demande doit être ré-authentifiée. Cette architecture
sans état fonctionne bien avec les API REST et leur contrainte d'absence d'état. Ils
fonctionnent également bien avec les mécanismes d'authentification tels que
l'authentification de base et Digest.
Formateur Jaouad Assabbour Avril 2022
17. @Override
protected void configure(HttpSecurity http) throws
Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STAT
ELESS)
}
authorizeRequests().antMatchers()
La authorizeRequests().antMatchers() est ensuite utilisée pour
appliquer l'autorisation d'un ou de plusieurs chemins spécifiés dans
antMatchers(). Comme permitAll().ou hasRole('USER3'). Ces
seulement appliquée si le premier http.antMatcher() est
mis en correspondance.
exemple :
http.authorizeRequests().antMatchers("/login*").permitAll();
http.authorizeRequests().antMatchers("/myUsers/**").hasAuthority("ADMIN");
anyRequest(). Authenticated()
anyRequest(). authenticated()limitera l'accès à tout autre point de terminaison autre
que PUBLIC_URL, et l'utilisateur doit être authentifié
Formateur Jaouad Assabbour Avril 2022
18. sucParam
avant créer nos filtres, on va créer une interface pour les paramètres, qu'on va utiliser
plusieurs fois
Formateur Jaouad Assabbour Avril 2022
19. JwtAuthenticationFilter()
la class contient deux méthodes
attemptAuthentication()
La fonction attemptAuthentication() s'exécute lorsque l'utilisateur tente de se
connecter à notre application. Il lit les informations d'identification, crée un
utilisateur POJO à partir de celles-ci, puis vérifie les informations d'identification
pour s'authentifier.
@Override
public Authentication
attemptAuthentication(HttpServletRequest request,
HttpServletResponse response)
throws AuthenticationException {
try {
MyUser myUser = new
ObjectMapper().readValue(request.getInputStream(),
MyUser.class);
return
authenticationManager.authenticate(new
UsernamePasswordAuthenticationToken(myUser.getUsername()
, myUser.getPassword()));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
Nous transmettons le nom d'utilisateur, le mot de passe et une liste vide. La liste vide
représente les autorités (rôles), et nous la laissons telle quelle puisque nous n'avons
pas encore de rôle dans notre application.
SuccessfulAuthentication()
Si l'authentification réussit, la méthode successAuthentication s'exécute. Les
paramètres de cette méthode sont passés par Spring Security dans les coulisses.
La méthode tryAuthentication renvoie un objet Authentication qui contient les
autorités que nous avons transmises lors de la tentative.
Formateur Jaouad Assabbour Avril 2022
20. Nous voulons renvoyer un jeton à l'utilisateur une fois l'authentification réussie, nous
créons donc le jeton en utilisant le nom d'utilisateur, le secret et la date d'expiration.
Nous devons définir SECRET et EXPIRATION_DATE maintenant.
@Override
protected void
successfulAuthentication(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain,
Authentication authResult) throws
IOException, ServletException {
User user = (User) authResult.getPrincipal();
List<String> roles = new ArrayList<>();
authResult.getAuthorities().forEach(a->{
roles.add(a.getAuthority());
});
String jwt = JWT.create()
.withIssuer(request.getRequestURI()
)
.withSubject(user.getUsername())
.withArrayClaim("roles",
roles.toArray(new String[roles.size()]))
.withExpiresAt(new
Date(System.currentTimeMillis()+SecuParams.EXPIRATION))
.sign(Algorithm.HMAC256(SecuParams.
SECRET));
response.addHeader(SecuParams.HEADER_NAME,jwt);
}
Formateur Jaouad Assabbour Avril 2022
22. JWTAuthorizationFilter
La méthode doFilterInternal intercepte les requêtes puis vérifie l'en-tête
Authorization. Si l'en-tête n'est pas présent ou ne commence pas par "BEARER", il
passe à la chaîne de filtrage.
Si l'en-tête est présent, la méthode getAuthentication est invoquée. getAuthentication
vérifie le JWT, et si le jeton est valide, il renvoie un jeton d'accès que Spring utilisera
en interne.
Ce nouveau jeton est ensuite enregistré dans SecurityContext. Vous pouvez
également transmettre des autorités à ce jeton si vous avez besoin d'une autorisation
basée sur les rôles.
Nos filtres sont prêts, et maintenant nous devons les mettre en action à l'aide d'une
classe de configuration.
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-
Headers",
"Origin, Accept,X-Request-With, Content-
Type, "
+ "Access-Control-Request-Method,
Access-Control-Request-Headers,authorization");
Formateur Jaouad Assabbour Avril 2022