Étape 1 : Création du microservice User
1. Modèle User
Création de l’entité User.java dans le package com.user.model :
package com.user.model;
import javax.persistence.*;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique = true, nullable = false)
private String username;
@Column(nullable = false)
private String password;
public User() {}
public User(String username, String password) {
this.username = username;
this.password = password;
}
// Getters et setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}2. Repository User
Création de UserRepository.java dans le package com.user.repository :
package com.user.repository;
import com.user.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByUsername(String username);
boolean existsByUsername(String username);
}3. Service User
Création de JwtTokenProvider.java dans le package com.user.security :
package com.user.security;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
@Component
public class JwtTokenProvider {
private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
private final long validityInMilliseconds = 3600000; // 1 heure
public String createToken(String username) {
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
return Jwts.builder()
.setSubject(username)
.setIssuedAt(now)
.setExpiration(validity)
.signWith(key)
.compact();
}
public String getUsernameFromToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}Création de UserService.java dans le package com.user.service :
package com.user.service;
import com.user.model.User;
import com.user.repository.UserRepository;
import com.user.security.JwtTokenProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private JwtTokenProvider jwtTokenProvider;
public User registerUser(String username, String password) {
if (userRepository.existsByUsername(username)) {
throw new RuntimeException("Username already exists");
}
String encodedPassword = passwordEncoder.encode(password);
User user = new User(username, encodedPassword);
return userRepository.save(user);
}
public String authenticateUser(String username, String password) {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new RuntimeException("User not found"));
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new RuntimeException("Invalid credentials");
}
return jwtTokenProvider.createToken(username);
}
}4. Contrôleur User
Création de UserController.java dans le package com.user.controller :
package com.user.controller;
import com.user.model.User;
import com.user.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public ResponseEntity<?> register(@RequestBody Map<String, String> credentials) {
try {
String username = credentials.get("username");
String password = credentials.get("password");
User user = userService.registerUser(username, password);
return ResponseEntity.ok(Map.of(
"message", "User registered successfully",
"userId", user.getId()
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"error", e.getMessage()
));
}
}
@PostMapping("/login")
public ResponseEntity<?> login(@RequestBody Map<String, String> credentials) {
try {
String username = credentials.get("username");
String password = credentials.get("password");
String token = userService.authenticateUser(username, password);
return ResponseEntity.ok(Map.of(
"token", token,
"message", "Authentication successful"
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"error", e.getMessage()
));
}
}
}5. JwtTokenProvider
Création de JwtTokenProvider.java dans le package com.user.security :
package com.user.security;
import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import org.springframework.stereotype.Component;
import java.security.Key;
import java.util.Date;
@Component
public class JwtTokenProvider {
private final Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
private final long validityInMilliseconds = 3600000; // 1 heure
public String createToken(String username) {
Date now = new Date();
Date validity = new Date(now.getTime() + validityInMilliseconds);
return Jwts.builder()
.setSubject(username)
.setIssuedAt(now)
.setExpiration(validity)
.signWith(key)
.compact();
}
public String getUsernameFromToken(String token) {
return Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token)
.getBody()
.getSubject();
}
public boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(key)
.build()
.parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
}Rôle : Génère et valide les tokens JWT pour l’authentification. La clé secrète est générée automatiquement avec l’algorithme HS256. La durée de validité est fixée à 1 heure.
Méthodes implémentées :
createToken: Crée un JWT contenant le username comme subject avec une date d’expirationgetUsernameFromToken: Extrait le username depuis un JWT validevalidateToken: Vérifie la validité et l’intégrité du token
6. Configuration (Application.properties et sécurité)
Création de SecurityConfig.java dans le package com.user.config :
package com.user.config;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.anyRequest().permitAll();
return http.build();
}
}Configuration application.properties :
server.port=8081
spring.application.name=user-service
# H2 Database
spring.datasource.url=jdbc:h2:mem:userdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# JPA
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.show-sql=true
# H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2-consoleFichier pom.xml avec les dépendances :
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
<scope>runtime</scope>
</dependency>
</dependencies>Classe principale UserServiceApplication.java :
package com.user;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
}Démarrage du service :
mvn spring-boot:runLe service User est accessible sur le port 8081.
Étape 2 : Création du microservice Order
1. Modèle Order
Création de l’entité Order.java dans le package com.order.model :
package com.order.model;
import javax.persistence.*;
@Entity
@Table(name = "orders")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private Long userId;
@Column(nullable = false)
private String product;
@Column(nullable = false)
private int quantity;
public Order() {}
public Order(Long userId, String product, int quantity) {
this.userId = userId;
this.product = product;
this.quantity = quantity;
}
// Getters et setters
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getProduct() {
return product;
}
public void setProduct(String product) {
this.product = product;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}2. Repository Order
Création de OrderRepository.java dans le package com.order.repository :
package com.order.repository;
import com.order.model.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByUserId(Long userId);
}Méthode personnalisée findByUserId pour récupérer toutes les commandes d’un utilisateur.
3. Service Order
Création de OrderService.java dans le package com.order.service :
package com.order.service;
import com.order.model.Order;
import com.order.repository.OrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class OrderService {
@Autowired
private OrderRepository orderRepository;
public Order createOrder(Long userId, String product, int quantity) {
Order order = new Order(userId, product, quantity);
return orderRepository.save(order);
}
public List<Order> getOrdersByUserId(Long userId) {
return orderRepository.findByUserId(userId);
}
public List<Order> getAllOrders() {
return orderRepository.findAll();
}
}4. Contrôleur Order
Création de OrderController.java dans le package com.order.controller :
package com.order.controller;
import com.order.model.Order;
import com.order.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping
public ResponseEntity<?> createOrder(@RequestBody Map<String, Object> orderData,
@RequestHeader(value = "Authorization", required = false) String authHeader) {
try {
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
return ResponseEntity.status(401).body(Map.of(
"error", "Missing or invalid authorization token"
));
}
Long userId = Long.valueOf(orderData.get("userId").toString());
String product = orderData.get("product").toString();
int quantity = Integer.parseInt(orderData.get("quantity").toString());
Order order = orderService.createOrder(userId, product, quantity);
return ResponseEntity.ok(Map.of(
"message", "Order created successfully",
"orderId", order.getId()
));
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"error", e.getMessage()
));
}
}
@GetMapping("/user/{userId}")
public ResponseEntity<?> getOrdersByUser(@PathVariable Long userId,
@RequestHeader(value = "Authorization", required = false) String authHeader) {
try {
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
return ResponseEntity.status(401).body(Map.of(
"error", "Missing or invalid authorization token"
));
}
List<Order> orders = orderService.getOrdersByUserId(userId);
return ResponseEntity.ok(orders);
} catch (Exception e) {
return ResponseEntity.badRequest().body(Map.of(
"error", e.getMessage()
));
}
}
@GetMapping
public ResponseEntity<?> getAllOrders() {
List<Order> orders = orderService.getAllOrders();
return ResponseEntity.ok(orders);
}
}Étape 3 : Création de l’API Gateway
1. Configuration du projet
Création du fichier application.yml :
server:
port: 8083
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
- id: user-service
uri: http://localhost:8085
predicates:
- Path=/api/auth/**
filters:
- StripPrefix=1
- id: order-service
uri: http://localhost:8082
predicates:
- Path=/api/orders/**
filters:
- name: JwtAuthorizationFilter
jwt:
secret: *********
expiration: 3600
header: Authorization
prefix: BearerÉtape 4 : Tests avec Postman
1. Inscription d’un utilisateur
Configuration de la requête Postman :
- Méthode : POST
- URL :
http://localhost:8085/api/users/register - Headers :
Content-Type: application/json - Body (raw JSON) :
{
"username": "John",
"password": "password123"
}Résultat obtenu :
{
"message": "User registered successfully",
"userId": 1
}L’utilisateur est créé avec succès dans la base H2 du User Service. L’API Gateway a correctement routé la requête vers le port 8081.
2. Connexion pour obtenir un JWT
Configuration de la requête :
- Méthode : POST
- URL :
http://localhost:8085/api/users/login - Headers :
Content-Type: application/json - Body :
{
"username": "John",
"password": "password123"
}Résultat :
{
"token": "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2huIiwiaWF0IjoxNzA0MjkwNDAwLCJleHAiOjE3MDQyOTQwMDB9.Xy9z3K8N5pQm7vL2sJ9hF4tR6wE8nC1mD0aB5kU7yI",
"message": "Authentication successful"
}Le JWT est généré avec succès. Ce token sera utilisé pour les requêtes suivantes nécessitant une authentification.
3. Ajout d’une commande avec JWT
Configuration de la requête :
- Méthode : POST
- URL :
http://localhost:8082/api/orders - Headers :
Content-Type: application/jsonAuthorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2huIiwiaWF0IjoxNzA0MjkwNDAwLCJleHAiOjE3MDQyOTQwMDB9.Xy9z3K8N5pQm7vL2sJ9hF4tR6wE8nC1mD0aB5kU7yI
- Body :
{
"userId": 1,
"product": "Laptop",
"quantity": 1
}Résultat :
{
"message": "Order created successfully",
"orderId": 1
}La commande est créée avec succès. Le contrôleur Order a vérifié la présence du token Bearer avant de traiter la requête.
4. Récupération des commandes d’un utilisateur
Configuration de la requête :
- Méthode : GET
- URL :
http://localhost:8082/api/orders/user/1 - Headers :
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2huIiwiaWF0IjoxNzA0MjkwNDAwLCJleHAiOjE3MDQyOTQwMDB9.Xy9z3K8N5pQm7vL2sJ9hF4tR6wE8nC1mD0aB5kU7yI
Résultat :
[
{
"id": 1,
"userId": 1,
"product": "Laptop",
"quantity": 1
}
]Les commandes de l’utilisateur sont récupérées correctement.