Spring Boot - OAuth2 avec JWT

Dans ce chapitre, vous apprendrez en détail les mécanismes de Spring Boot Security et OAuth2 avec JWT.

Serveur d'autorisation

Authorization Server est un composant architectural suprême pour la sécurité des API Web. Le serveur d'autorisation agit comme un point d'autorisation de centralisation qui permet à vos applications et points de terminaison HTTP d'identifier les fonctionnalités de votre application.

Serveur de ressources

Resource Server est une application qui fournit le jeton d'accès aux clients pour accéder aux points de terminaison HTTP Resource Server. C'est une collection de bibliothèques qui contient les points de terminaison HTTP, les ressources statiques et les pages Web dynamiques.

OAuth2

OAuth2 est une infrastructure d'autorisation qui permet à l'application Web Security d'accéder aux ressources à partir du client. Pour créer une application OAuth2, nous devons nous concentrer sur le type de subvention (code d'autorisation), l'ID client et le secret client.

 

Jeton JWT

Le jeton JWT est un jeton Web JSON, utilisé pour représenter les revendications sécurisées entre deux parties. Vous pouvez en savoir plus sur le jeton JWT sur www.jwt.io/ .

Maintenant, nous allons créer une application OAuth2 qui permet l'utilisation du serveur d'autorisation, serveur de ressources à l'aide d'un jeton JWT.

Vous pouvez utiliser les étapes suivantes pour implémenter le jeton Spring Boot Security avec JWT en accédant à la base de données.

Tout d'abord, nous devons ajouter les dépendances suivantes dans notre fichier de configuration de construction.

Les utilisateurs Maven peuvent ajouter les dépendances suivantes dans votre fichier pom.xml.

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.security.oauth</groupId>
   <artifactId>spring-security-oauth2</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-jwt</artifactId>
</dependency>

<dependency>
   <groupId>com.h2database</groupId>
   <artifactId>h2</artifactId>
</dependency>

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-test</artifactId>
   <scope>test</scope>
</dependency>

<dependency>
   <groupId>org.springframework.security</groupId>
   <artifactId>spring-security-test</artifactId>
   <scope>test</scope>
</dependency>

Les utilisateurs Gradle peuvent ajouter les dépendances suivantes dans le fichier build.gradle.

compile('org.springframework.boot:spring-boot-starter-security')
compile('org.springframework.boot:spring-boot-starter-web')
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('org.springframework.security:spring-security-test')

compile("org.springframework.security.oauth:spring-security-oauth2")
compile('org.springframework.security:spring-security-jwt')
compile("org.springframework.boot:spring-boot-starter-jdbc")
compile("com.h2database:h2:1.4.191")

où,

  • Spring Boot Starter Security - Implémente la sécurité Spring

  • Spring Security OAuth2 - Implémente la structure OAUTH2 pour activer le serveur d'autorisation et le serveur de ressources.

  • Spring Security JWT - Génère le jeton JWT pour la sécurité Web

  • Spring Boot Starter JDBC - Accède à la base de données pour s'assurer que l'utilisateur est disponible ou non.

  • Spring Boot Starter Web - Écrit les points de terminaison HTTP.

  • H2 Database - Stocke les informations utilisateur pour l'authentification et l'autorisation.

Le fichier de configuration de construction complet est donné ci-dessous.

<?xml version = "1.0" encoding = "UTF-8"?>
<project xmlns = "http://maven.apache.org/POM/4.0.0" 
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 
   http://maven.apache.org/xsd/maven-4.0.0.xsd">
   
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.tutorialspoint</groupId>
   <artifactId>websecurityapp</artifactId>
   <version>0.0.1-SNAPSHOT</version>
   <packaging>jar</packaging>
   <name>websecurityapp</name>
   <description>Demo project for Spring Boot</description>

   <parent>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-parent</artifactId>
      <version>1.5.9.RELEASE</version>
      <relativePath /> <!-- lookup parent from repository -->
   </parent>

   <properties>
      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
      <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
      <java.version>1.8</java.version>
   </properties>

   <dependencies>
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-jdbc</artifactId>
      </dependency>
      
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-security</artifactId>
      </dependency>
      
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-web</artifactId>
      </dependency>
      
      <dependency>
         <groupId>org.springframework.security.oauth</groupId>
         <artifactId>spring-security-oauth2</artifactId>
      </dependency>
      
      <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-jwt</artifactId>
      </dependency>
      
      <dependency>
         <groupId>com.h2database</groupId>
         <artifactId>h2</artifactId>
      </dependency>
      
      <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-test</artifactId>
         <scope>test</scope>
      </dependency>
      
      <dependency>
         <groupId>org.springframework.security</groupId>
         <artifactId>spring-security-test</artifactId>
         <scope>test</scope>
      </dependency>
   </dependencies>

   <build>
      <plugins>
         <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
         </plugin>
      </plugins>
   </build>
   
</project>

Gradle – build.gradle

buildscript {
   ext {
      springBootVersion = '1.5.9.RELEASE'
   }
   repositories {
      mavenCentral()
   }
   dependencies {
      classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
   }
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

group = 'com.tutorialspoint'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
   mavenCentral()
}

dependencies {
   compile('org.springframework.boot:spring-boot-starter-security')
   compile('org.springframework.boot:spring-boot-starter-web')
   testCompile('org.springframework.boot:spring-boot-starter-test')
   testCompile('org.springframework.security:spring-security-test')
   compile("org.springframework.security.oauth:spring-security-oauth2")
   compile('org.springframework.security:spring-security-jwt')
   compile("org.springframework.boot:spring-boot-starter-jdbc")
   compile("com.h2database:h2:1.4.191")  
}

Maintenant, dans l'application Spring Boot principale, ajoutez les annotations @EnableAuthorizationServer et @EnableResourceServer pour agir en tant que serveur d'authentification et serveur de ressources dans la même application.

En outre, vous pouvez utiliser le code suivant pour écrire un point de terminaison HTTP simple pour accéder à l'API avec Spring Security à l'aide du jeton JWT.

package com.tutorialspoint.websecurityapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@EnableAuthorizationServer
@EnableResourceServer
@RestController
public class WebsecurityappApplication {
   public static void main(String[] args) {
      SpringApplication.run(WebsecurityappApplication.class, args);
   }
   @RequestMapping(value = "/products")
   public String getProductName() {
      return "Honey";   
   }
}

Utilisez le code suivant pour définir la classe POJO afin de stocker les informations utilisateur pour l'authentification.

package com.tutorialspoint.websecurityapp;

import java.util.ArrayList;
import java.util.Collection;
import org.springframework.security.core.GrantedAuthority;

public class UserEntity {
   private String username;
   private String password;
   private Collection<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();
   
   public String getPassword() {
      return password;
   }
   public void setPassword(String password) {
      this.password = password;
   }
   public Collection<GrantedAuthority> getGrantedAuthoritiesList() {
      return grantedAuthoritiesList;
   }
   public void setGrantedAuthoritiesList(Collection<GrantedAuthority> grantedAuthoritiesList) {
      this.grantedAuthoritiesList = grantedAuthoritiesList;
   }
   public String getUsername() {
      return username;
   }
   public void setUsername(String username) {
      this.username = username;
   }
}

Maintenant, utilisez le code suivant et définissez la classe CustomUser qui étend la classe org.springframework.security.core.userdetails.User pour l'authentification Spring Boot.

package com.tutorialspoint.websecurityapp;

import org.springframework.security.core.userdetails.User;

public class CustomUser extends User {
   private static final long serialVersionUID = 1L;
   public CustomUser(UserEntity user) {
      super(user.getUsername(), user.getPassword(), user.getGrantedAuthoritiesList());
   }
}

Vous pouvez créer la classe @Repository pour lire les informations utilisateur de la base de données et les envoyer au service utilisateur personnalisé et également ajouter l'autorité accordée «ROLE_SYSTEMADMIN».

package com.tutorialspoint.websecurityapp;

import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Repository;

@Repository
public class OAuthDao {
   @Autowired
   private JdbcTemplate jdbcTemplate;

   public UserEntity getUserDetails(String username) {
      Collection<GrantedAuthority> grantedAuthoritiesList = new ArrayList<>();
      String userSQLQuery = "SELECT * FROM USERS WHERE USERNAME=?";
      List<UserEntity> list = jdbcTemplate.query(userSQLQuery, new String[] { username },
         (ResultSet rs, int rowNum) -> {
         
         UserEntity user = new UserEntity();
         user.setUsername(username);
         user.setPassword(rs.getString("PASSWORD"));
         return user;
      });
      if (list.size() > 0) {
         GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_SYSTEMADMIN");
         grantedAuthoritiesList.add(grantedAuthority);
         list.get(0).setGrantedAuthoritiesList(grantedAuthoritiesList);
         return list.get(0);
      }
      return null;
   }
}

Vous pouvez créer une classe de service de détail utilisateur personnalisé qui étend org.springframework.security.core.userdetails.UserDetailsService pour appeler la classe de référentiel DAO comme indiqué.

package com.tutorialspoint.websecurityapp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class CustomDetailsService implements UserDetailsService {
   @Autowired
   OAuthDao oauthDao;

   @Override
   public CustomUser loadUserByUsername(final String username) throws UsernameNotFoundException {
      UserEntity userEntity = null;
      try {
         userEntity = oauthDao.getUserDetails(username);
         CustomUser customUser = new CustomUser(userEntity);
         return customUser;
      } catch (Exception e) {
         e.printStackTrace();
         throw new UsernameNotFoundException("User " + username + " was not found in the database");
      }
   }
}

Ensuite, créez une classe @configuration pour activer la sécurité Web, en définissant l'encodeur de mot de passe (BCryptPasswordEncoder) et en définissant le bean AuthenticationManager. La classe de configuration de sécurité doit étendre la classe WebSecurityConfigurerAdapter.

package com.tutorialspoint.websecurityapp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
   @Autowired
   private CustomDetailsService customDetailsService;

   @Bean
   public PasswordEncoder encoder() {
      return new BCryptPasswordEncoder();
   }
   @Override
   @Autowired
   protected void configure(AuthenticationManagerBuilder auth) throws Exception {
      auth.userDetailsService(customDetailsService).passwordEncoder(encoder());
   }
   @Override
   protected void configure(HttpSecurity http) throws Exception {
      http.authorizeRequests().anyRequest().authenticated().and().sessionManagement()
         .sessionCreationPolicy(SessionCreationPolicy.NEVER);
   }
   @Override
   public void configure(WebSecurity web) throws Exception {
      web.ignoring();
   }
   @Override
   @Bean
   public AuthenticationManager authenticationManagerBean() throws Exception {
      return super.authenticationManagerBean();
   }
}

Maintenant, définissez la classe de configuration OAuth2 pour ajouter l'ID client, le secret du client, définissez le JwtAccessTokenConverter, la clé privée et la clé publique pour la clé de signataire de jeton et la clé de vérificateur, et configurez le ClientDetailsServiceConfigurer pour la validité du jeton avec des étendues.

package com.tutorialspoint.websecurityapp;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
   private String clientid = "tutorialspoint";
   private String clientSecret = "my-secret-key";
   private String privateKey = "private key";
   private String publicKey = "public key";

   @Autowired
   @Qualifier("authenticationManagerBean")
   private AuthenticationManager authenticationManager;
   
   @Bean
   public JwtAccessTokenConverter tokenEnhancer() {
      JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
      converter.setSigningKey(privateKey);
      converter.setVerifierKey(publicKey);
      return converter;
   }
   @Bean
   public JwtTokenStore tokenStore() {
      return new JwtTokenStore(tokenEnhancer());
   }
   @Override
   public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
      endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore())
      .accessTokenConverter(tokenEnhancer());
   }
   @Override
   public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
      security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
   }
   @Override
   public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
      clients.inMemory().withClient(clientid).secret(clientSecret).scopes("read", "write")
         .authorizedGrantTypes("password", "refresh_token").accessTokenValiditySeconds(20000)
         .refreshTokenValiditySeconds(20000);

   }
}

Maintenant, créez une clé privée et une clé publique en utilisant openssl.

Vous pouvez utiliser les commandes suivantes pour générer la clé privée.

openssl genrsa -out jwt.pem 2048
openssl rsa -in jwt.pem

Vous pouvez utiliser Pour la génération de clé publique, utilisez les commandes ci-dessous.

openssl rsa -in jwt.pem -pubout

Pour la version de Spring Boot postérieure à la version 1.5, ajoutez la propriété ci-dessous dans votre fichier application.properties pour définir l'ordre de filtrage des ressources OAuth2.

security.oauth2.resource.filter-order=3

Les utilisateurs du fichier YAML peuvent ajouter la propriété ci-dessous dans le fichier YAML.

security:
   oauth2:
      resource:
         filter-order: 3

Maintenant, créez le fichier schema.sql et data.sql sous les ressources classpath src/main/resources/directory pour connecter l'application à la base de données H2.

Le fichier schema.sql est comme indiqué -

CREATE TABLE USERS (ID INT PRIMARY KEY, USERNAME VARCHAR(45), PASSWORD VARCHAR(60));

Le fichier data.sql est comme indiqué -

INSERT INTO USERS (ID, USERNAME,PASSWORD) VALUES (
   1, '[email protected]','$2a$08$fL7u5xcvsZl78su29x1ti.dxI.9rYO8t0q5wk2ROJ.1cdR53bmaVG');

INSERT INTO USERS (ID, USERNAME,PASSWORD) VALUES (
   2, '[email protected]','$2a$08$fL7u5xcvsZl78su29x1ti.dxI.9rYO8t0q5wk2ROJ.1cdR53bmaVG');

Note - Le mot de passe doit être stocké au format Bcrypt Encoder dans la table de base de données.

Vous pouvez créer un fichier JAR exécutable et exécuter l'application Spring Boot à l'aide des commandes Maven ou Gradle suivantes.

Pour Maven, vous pouvez utiliser la commande donnée ci-dessous -

mvn clean install

Après «BUILD SUCCESS», vous pouvez trouver le fichier JAR sous le répertoire cible.

Pour Gradle, vous pouvez utiliser la commande comme indiqué -

gradle clean build

Après «BUILD SUCCESSFUL», vous pouvez trouver le fichier JAR dans le répertoire build / libs.

Maintenant, exécutez le fichier JAR en utilisant la commande indiquée ici -

java –jar <JARFILE>

L'application est lancée sur le port Tomcat 8080.

Cliquez maintenant sur l'URL de la méthode POST via POSTMAN pour obtenir le jeton OAUTH2.

http://localhost:8080/oauth/token

Maintenant, ajoutez les en-têtes de demande comme suit -

  • Authorization - Authentification de base avec votre identifiant client et votre secret client.

  • Content Type - application / x-www-form-urlencoded

Maintenant, ajoutez les paramètres de demande comme suit -

  • grant_type = mot de passe
  • nom d'utilisateur = votre nom d'utilisateur
  • mot de passe = votre mot de passe

Maintenant, appuyez sur l'API et obtenez le access_token comme indiqué -

Maintenant, appuyez sur l'API du serveur de ressources avec le jeton d'accès au porteur dans l'en-tête de la demande, comme indiqué.

Ensuite, vous pouvez voir la sortie comme indiqué ci-dessous -