Bonjour à tous! En partant pour le week-end, nous partageons avec vous un article qui a été traduit avant le début du cours "Developer on the Spring Framework" .
Dans les articles précédents, nous avons créé un service Web RESTful, maintenant nous allons parler de sécuritéintroduction
Dans un article prĂ©cĂ©dent, nous avons examinĂ© comment crĂ©er une API REST Ă l'aide des frameworks Java Spring Boot et MongoDB. L'API, cependant, ne nĂ©cessitait aucune authentification, ce qui signifie qu'elle n'est probablement pas encore prĂȘte Ă l'emploi. Par consĂ©quent, ce guide vous montrera comment utiliser l'environnement de sĂ©curitĂ© intĂ©grĂ© de Spring pour ajouter un niveau d'authentification Ă cette API.Pourquoi notre API a-t-elle besoin d'une authentification?
Les API fournissent une interface simple pour interagir avec les données internes, il est donc logique que vous ne vouliez pas que quiconque ait accÚs à ces données et les modifie. L'authentification garantit que seuls les utilisateurs fiables peuvent accéder à l'API.Comment ça fonctionne
Nous utiliserons l' authentification HTTP de base , qui utilise un nom d'utilisateur et un mot de passe. Le nom d'utilisateur et le mot de passe sont sĂ©parĂ©s sur une ligne par deux points au format suivant username:password.Cette ligne est ensuite encodĂ©e en utilisant l'encodage Base64 , donc la ligne admin:p@55w0Rdsera encodĂ©e dans la ligne suivante YWRtaW46cEA1NXcwUmQ=(bien que je suggĂšre d'utiliser un mot de passe plus fort que «p @ 55w0Rd»). Nous pouvons attacher cette authentification Ă nos demandes en ajoutant un en-tĂȘte Authentication. Cet en-tĂȘte pour l'exemple prĂ©cĂ©dent ressemblerait Ă ceci (oĂč «de base» signifie que le mot de passe utilise l'authentification HTTP de base):Authentication: Basic YWRtaW46cEA1NXcwUmQ=
Comment Spring gÚre la sécurité
Spring propose un complĂ©ment appelĂ© Spring Security , qui rend l'authentification hautement personnalisable et extrĂȘmement simple. Nous pouvons mĂȘme utiliser certaines des compĂ©tences que nous avons apprises dans un post prĂ©cĂ©dent lors de la configuration!De quoi avons nous besoin
- Une nouvelle collection dans notre instance MongoDB appelée "utilisateurs"
- Un nouveau document dans la collection des utilisateurs avec les champs suivants (tous les autres champs sont facultatifs, mais ceux-ci sont nécessaires): nom d'utilisateur, mot de passe (haché en utilisant l'algorithme BCrypt, plus à ce sujet plus tard)
- Sources du post précédent
BCrypt pour le hachage de mot de passe
Le hachage est un algorithme de chiffrement Ă sens unique. En fait, aprĂšs le hachage, il est presque impossible de dĂ©couvrir Ă quoi ressemblaient les donnĂ©es d'origine. L'algorithme de hachage BCrypt salit d'abord un morceau de texte, puis le hache en une chaĂźne de 60 caractĂšres. L'encodeur Java BCrypt propose une mĂ©thode matchesqui vĂ©rifie si une chaĂźne correspond Ă un hachage. Par exemple, un mot de passe p@55w0RdhachĂ© avec BCrypt peut avoir une signification $2b$10$Qrc6rGzIGaHpbgPM5kVXdeNZ9NiyRWC69Wk/17mttHKnDR2lW49KS. Lors de l'appel de la mĂ©thode matchesBCrypt pour un mot de passe non chiffrĂ© et hachĂ©, nous obtenons une valeur true. Ces hachages peuvent ĂȘtre gĂ©nĂ©rĂ©s Ă l'aide de l'encodeur BCrypt intĂ©grĂ© Ă Spring Security.Pourquoi devrions-nous hacher les mots de passe?
Nous avons tous entendu parler de cyberattaques rĂ©centes qui ont entraĂźnĂ© le vol de mots de passe de grandes entreprises. Alors pourquoi est-il seulement recommandĂ© de changer nos mots de passe aprĂšs le piratage? Parce que ces grandes entreprises ont fait en sorte que les mots de passe soient toujours hachĂ©s dans leurs bases de donnĂ©es!Bien qu'il vaille toujours la peine de changer de mot de passe aprĂšs de tels hacks de donnĂ©es, le hachage de mot de passe rend extrĂȘmement difficile la recherche du vrai mot de passe de l'utilisateur, car il s'agit d'un algorithme Ă sens unique. En fait, cela peut prendre des annĂ©es pour dĂ©chiffrer correctement un mot de passe complexe hachĂ©. Cela offre un niveau de protection supplĂ©mentaire contre le vol de mot de passe. Et Spring Security simplifie le hachage, donc la vraie question devrait ĂȘtre: "Pourquoi pas?"Ajout d'un utilisateur Ă MongoDB
J'ajouterai un minimum de champs nécessaires à ma collection users(utilisateurs), donc un document avec des utilisateurs dans ma base de données ne contiendra que username(nom d'utilisateur) et haché BCrypt password(mot de passe). Dans cet exemple, mon nom d'utilisateur sera adminet mon mot de passe le sera welcome1, mais je suggérerais d'utiliser un nom d'utilisateur et un mot de passe plus robustes dans l'API de niveau de production.db.users.insert({
âusernameâ : âadminâ,
âpasswordâ : â$2a$10$AjHGc4x3Nez/p4ZpvFDWeO6FGxee/cVqj5KHHnHfuLnIOzC5ag4fmâ
});
Ce sont tous les paramÚtres nécessaires dans MongoDB! Le reste de la configuration se fera dans notre code Java.Ajout d'un modÚle utilisateur et d'un référentiel
Le post prĂ©cĂ©dent dĂ©crit en dĂ©tail les modĂšles et les rĂ©fĂ©rentiels de Mongo, donc je n'entrerai pas dans les dĂ©tails sur leur fonctionnement ici - si vous voulez rafraĂźchir vos connaissances, n'hĂ©sitez pas Ă visiter mon post prĂ©cĂ©dent!L'inconvĂ©nient est que Spring doit savoir Ă quoi ressemblera le document user(modĂšle) et comment accĂ©der Ă la collection userdans la base de donnĂ©es (rĂ©fĂ©rentiels). Nous pouvons placer ces fichiers dans les mĂȘmes dossiers de modĂšles et de rĂ©fĂ©rentiels, respectivement, comme nous l'avons fait dans l'exercice prĂ©cĂ©dent.ModĂšle
Le modÚle sera une classe de base Java avec custom _id, usernameet password. Le fichier sera nommé Users.java. et ressemblera à ceci:package com.example.gtommee.rest_tutorial.models;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
public class Users {
@Id
public ObjectId _id;
public String username;
public String password;
public Users() {}
public Users(ObjectId _id, String username, String password)
{
this._id = _id;
this.username = username;
this.password = password;
}
public void set_id(ObjectId _id) { this._id = _id; }
public String get_id() { return this._id.toHexString(); }
public void setPassword(String password) { this.password = password; }
public String getPassword() { return password; }
public void setUsername(String username) { this.username = username; }
public String getUsername() { return username; }
}
DépÎt
Le référentiel sera appelé UsersRepository.javaet ressemblera à ceci - rappelez-vous, nous devrons trouver des utilisateurs par eux username, nous devrons donc inclure la méthode findByUsernamedans l'interface du référentiel.package com.example.gtommee.rest_tutorial.repositories;
import com.example.gtommee.rest_tutorial.models.Users;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface UsersRepository extends MongoRepository<Users, String> {
Users findByUsername(String username);
}
Et c'est tout pour le modÚle et le référentiel!Ajout de dépendances de sécurité
Il devrait y avoir un fichier avec un nom dans le répertoire racine du projet pom.xml. Nous n'avons pas encore touché ce fichier, mais le fichier pom contient toutes les dépendances de notre projet, et nous allons en ajouter quelques-unes, alors commençons par ouvrir ce fichier et faire défiler jusqu'à la balise . La seule nouvelle dépendance dont nous avons besoin est la sécurité du démarreur à ressort . Spring a un gestionnaire de versions intégré, donc la dépendance que nous devons ajouter à la balise est la suivante:<dependencies><dependencies><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
Et Maven tĂ©lĂ©chargera les fichiers source pour nous, donc nos dĂ©pendances doivent ĂȘtre prĂȘtes Ă l'emploi!CrĂ©er un service d'authentification
Nous devons indiquer Ă Spring oĂč se trouvent nos donnĂ©es utilisateur et oĂč trouver les informations nĂ©cessaires Ă l'authentification. Pour ce faire, nous pouvons crĂ©er un service d'authentification (Service d'authentification). Commençons par crĂ©er un nouveau dossier dans les src/main/resources/java/[package name]services appelĂ©s, et nous pouvons crĂ©er un nouveau fichier dans ce dossier de configuration avec le nom MongoUserDetailsService.java.MongoUserDetailsService.java
Cette classe a un composant principal, je vais donc donner toute la classe ici et l'expliquer ci-dessous:package com.example.gtommee.rest_tutorial.services;
import com.example.gtommee.rest_tutorial.models.Users;
import com.example.gtommee.rest_tutorial.repositories.UsersRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class MongoUserDetailsService implements UserDetailsService{
@Autowired
private UsersRepository repository;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Users user = repository.findByUsername(username);
if(user == null) {
throw new UsernameNotFoundException(âUser not foundâ);
}
List<SimpleGrantedAuthority> authorities = Arrays.asList(new SimpleGrantedAuthority(âuserâ));
return new User(user.getUsername(), user.getPassword(), authorities);
}
}
Cet extrait commence par les importations dont nous avons besoin dans le fichier. De plus, la section implements UserDetailsServiceindique que cette classe crĂ©era un service de recherche et d'authentification d'utilisateurs. Ensuite, l'annotation @Componentindique que cette classe peut ĂȘtre incorporĂ©e dans un autre fichier (par exemple, le fichier SecurityConfiguration, que nous allons parcourir en plusieurs sections).Annotation @AutowiredterminĂ©e private UsersRepository repository; est un exemple d'implĂ©mentation, cette propriĂ©tĂ© nous fournit une instance de la nĂŽtre UsersRepositorypour le travail. L'annotation @Overrideindique que cette mĂ©thode sera utilisĂ©e Ă la place de la mĂ©thode UserDetailsService par dĂ©faut. Tout d'abord, cette mĂ©thode obtient l'objet Ă Userspartir de la source de donnĂ©es MongoDB en utilisant la mĂ©thode findByUsernameque nous avons dĂ©clarĂ©e dans UsersRepository.La mĂ©thode vĂ©rifie ensuite si l'utilisateur a Ă©tĂ© trouvĂ© ou non. Ensuite, l'utilisateur se voit accorder des autorisations / rĂŽles (cela peut ajouter des niveaux d'authentification supplĂ©mentaires pour les niveaux d'accĂšs, mais un rĂŽle sera suffisant pour cette leçon). Enfin, la mĂ©thode renvoie un objet printemps Useravec username, passwordet l' roleutilisateur authentifiĂ©.CrĂ©er une configuration de sĂ©curitĂ©
Nous devrons redéfinir certains des protocoles de sécurité intégrés de Spring pour utiliser notre base de données et notre algorithme de hachage, nous avons donc besoin d'un fichier de configuration spécial. Pour le créer, nous devons créer un nouveau dossier src/main/resources/java/[package name]avec le nom config, et nous devons également créer un nouveau fichier dans ce dossier de configuration avec le nom SecurityConfiguration.java. Ce fichier comporte plusieurs parties importantes, commençons donc par la classe de base SecurityConfiguration:SecurityConfiguration.java
package com.example.gtommee.rest_tutorial.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableConfigurationProperties
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
MongoUserDetailsService userDetailsService;
}
Il y a dĂ©jĂ assez de choses Ă traiter, alors commençons par le haut. Une annotation @Configurationindique que la classe contiendra des beans Java, dĂ©crits en dĂ©tail ici. Une annotation @EnableConfigurationPropertiesindique ce que la classe contiendra en tant que bean de configuration spĂ©cial. Ensuite, l'instruction extends WebSecurityConfigurerAdaptermappera la classe parente WebSecurityConfigurerAdapterĂ notre classe de configuration , fournissant Ă notre classe tout le nĂ©cessaire pour garantir le respect de ses rĂšgles de sĂ©curitĂ©. Enfin, la classe injectera automatiquement une instance ( @Autowired) MongoUserDetailsService, que nous pourrons utiliser plus loin dans ce fichier.Ătape d'authentification
Ensuite, nous devons indiquer à Spring Security comment nous voulons gérer l'authentification des utilisateurs. Par défaut, Spring Security a un nom d'utilisateur et un mot de passe prédéfinis, une protection CSRF et une gestion de session . Cependant, nous voulons que nos utilisateurs utilisent leur nom d'utilisateur et leur mot de passe pour accéder à la base de données. De plus, puisque nos utilisateurs seront réauthentifiés à chaque demande, plutÎt que de se connecter, nous n'avons pas besoin de protection CSRF et de gestion de session, nous pouvons donc ajouter une méthode avec un nom configurequi remplace le schéma d'authentification par défaut pour dire à Spring exactement comment nous voulons gérer l'authentification, et ressemblera à ceci:@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and().httpBasic()
.and().sessionManagement().disable();
}
Encore une fois, il se passe beaucoup de choses ici, donc nous allons rĂ©gler cela par Ă©tapes. L'annotation @Overrideindique Ă Spring Boot d'utiliser la mĂ©thode configure (HttpSecurity http)au lieu de la configuration Spring par dĂ©faut. Ensuite, nous appelons une sĂ©rie de mĂ©thodes pour l'objet httpoĂč la configuration rĂ©elle a lieu. Ces mĂ©thodes procĂšdent comme suit:csrf().disable(): DĂ©sactive la protection CSRF car elle n'est pas nĂ©cessaire pour l'APIauthorizeRequests().anyRequest().authenticated(): DĂ©clare que toutes les demandes adressĂ©es Ă un noeud final doivent ĂȘtre autorisĂ©es, sinon elles doivent ĂȘtre rejetĂ©es.and().httpBasic(): Springde sorte qu'il attend l'authentification HTTP de base (discutĂ©e ci-dessus)..and().sessionManagement().disable(): indique Ă Spring de ne pas stocker les informations de session pour les utilisateurs, car cela n'est pas nĂ©cessaire pour l'API
Ajout d'un encodeur Bcrypt
Maintenant, nous devons dire Ă Spring d'utiliser l'encodeur BCrypt pour hacher et comparer les mots de passe - cela semble ĂȘtre une tĂąche difficile, mais en fait, c'est trĂšs simple. Nous pouvons ajouter cet encodeur en ajoutant simplement les lignes suivantes Ă notre classe SecurityConfiguration:@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Et tout le travail! Ce bean simple indique à Spring que le PasswordEncoder que nous voulons utiliser est Spring Boot BCryptPasswordEncoder()pour encoder et comparer les hachages de mot de passe. Spring Boot comprend également plusieurs autres encodeurs de mot de passe - je vous recommande de les essayer si vous voulez expérimenter!Spécifiez le gestionnaire d'authentification
Enfin, nous devons indiquer dans notre SecurityConfigurationque nous voulons utiliser MongoUserDetailsService(que nous avons créé dans la section précédente) pour notre authentification. Nous pouvons le faire en utilisant la méthode suivante:@Override
public void configure(AuthenticationManagerBuilder builder)
throws Exception {
builder.userDetailsService(userDetailsService);
}
Cette méthode remplace simplement la configuration par défaut AuthenticationManagerBuilder, remplaçant plutÎt notre propre service de transfert de données personnalisé.Fichier final SecurityConfiguration.java
import com.example.gtommee.rest_tutorial.services.MongoUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableConfigurationProperties
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
MongoUserDetailsService userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests().anyRequest().authenticated()
.and().httpBasic()
.and().sessionManagement().disable();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
public void configure(AuthenticationManagerBuilder builder)
throws Exception {
builder.userDetailsService(userDetailsService);
}
}
ContrĂŽle d'authentification
Je vais tester une GETdemande rapide avec une authentification valide et incorrecte pour m'assurer que la configuration fonctionne comme prévu. URL/ nom d'utilisateur / mot de passe incorrect: http://localhost:8080/pets/Méthode: GETConnexion: Basic YWRtaW46d2VsY29tZQ==Réponse:401 Unauthorized
URL de nom d' utilisateur / mot de passe valide : http://localhost:8080/pets/Méthode: GETConnexion: Basic YWRtaW46d2VsY29tZTE=Réponse:[
{
â_idâ: â5aeccb0a18365ba07414356câ,
ânameâ: âSpotâ,
âspeciesâ: âdogâ,
âbreedâ: âpitbullâ
},
{
â_idâ: â5aeccb0a18365ba07414356dâ,
ânameâ: âDaisyâ,
âspeciesâ: âcatâ,
âbreedâ: âcalicoâ
},
{
â_idâ: â5aeccb0a18365ba07414356eâ,
ânameâ: âBellaâ,
âspeciesâ: âdogâ,
âbreedâ: âaustralian shepardâ
}
]
Conclusion
Cela fonctionne comme il se doit! L'authentification HTTP de base pour l'API Spring Boot peut ĂȘtre dĂ©licate, mais nous espĂ©rons que ce guide contribuera Ă la rendre plus comprĂ©hensible. L'authentification est un incontournable dans le cyber-climat d'aujourd'hui, donc des outils comme Spring Security sont essentiels pour garantir l'intĂ©gritĂ© et la sĂ©curitĂ© de vos donnĂ©es.C'est tout! Retrouvez-moi sur le parcours .