security: remove credentials from docs, centralize CORS, add reverse proxy support

This commit is contained in:
Nabil Derouiche 2026-04-19 21:56:08 +01:00
parent f40888e7ee
commit 088f2b8736
21 changed files with 40 additions and 21 deletions

3
.gitignore vendored
View File

@ -37,5 +37,8 @@ frontend/.flutter-plugins
frontend/.flutter-plugins-dependencies frontend/.flutter-plugins-dependencies
frontend/build/ frontend/build/
# Claude Code (contient des tokens et commandes sensibles)
.claude/
# Docs sensibles # Docs sensibles
memoire de fin d'etude.docx memoire de fin d'etude.docx

View File

@ -14,7 +14,7 @@
| 3 | Backend Spring Boot API | ✅ Déployé & Fonctionnel | | 3 | Backend Spring Boot API | ✅ Déployé & Fonctionnel |
| 4 | Frontend Flutter | ⏳ À faire | | 4 | Frontend Flutter | ⏳ À faire |
| 5 | Tests & Validation | 🔄 En cours | | 5 | Tests & Validation | 🔄 En cours |
| 6 | Déploiement Production | ✅ Docker sur 192.168.100.33:8090 | | 6 | Déploiement Production | ✅ Docker — https://rayhan-erp.bolbol.tn |
| 7 | Rapport de PFE | 🔄 En cours | | 7 | Rapport de PFE | 🔄 En cours |
--- ---
@ -88,11 +88,9 @@
## Infrastructure Serveur ## Infrastructure Serveur
- **Serveur local** : 192.168.100.33
- **SSH** : port 22222, user Best0f
- **Portainer** : http://192.168.100.33:9000/
- **Gitea** : https://gitea.bolbol.tn - **Gitea** : https://gitea.bolbol.tn
- **API REST** : http://192.168.100.33:8090 ✅ En ligne - **API REST (production)** : https://rayhan-erp.bolbol.tn ✅ En ligne
- **API REST (local dev)** : http://localhost:8090
- **Conteneurs Docker** : `rayhan-mysql` + `rayhan-backend` - **Conteneurs Docker** : `rayhan-mysql` + `rayhan-backend`
--- ---

View File

@ -179,7 +179,7 @@ Password : rayhan_erp_2024
> MySQL Workbench : https://dev.mysql.com/downloads/workbench/ > MySQL Workbench : https://dev.mysql.com/downloads/workbench/
**Note :** Sur le serveur de production (192.168.100.33), le port MySQL n'est pas exposé à l'extérieur par mesure de sécurité. **Note :** Sur le serveur de production (localhost), le port MySQL n'est pas exposé à l'extérieur par mesure de sécurité.
--- ---

View File

@ -3,12 +3,12 @@
## Configuration de Base ## Configuration de Base
**URL de base** : `http://192.168.100.33:8090` **URL de base** : `https://rayhan-erp.bolbol.tn`
### 1. Configurer une Variable d'Environnement Postman ### 1. Configurer une Variable d'Environnement Postman
Dans Postman, créer un environnement "Rayhan ERP" avec : Dans Postman, créer un environnement "Rayhan ERP" avec :
- `baseUrl` = `http://192.168.100.33:8090` - `baseUrl` = `https://rayhan-erp.bolbol.tn`
- `token` = (sera rempli automatiquement) - `token` = (sera rempli automatiquement)
--- ---

View File

@ -204,7 +204,7 @@ La base de données `rayhan_erp_db` contient les tables suivantes :
## 4.1 Infrastructure ## 4.1 Infrastructure
L'application est déployée sur un serveur NAS Synology (192.168.100.33) via Docker Compose. Deux conteneurs sont en production : L'application est déployée via Docker Compose, accessible publiquement derrière un reverse proxy HTTPS. Deux conteneurs sont en production :
- **rayhan-mysql** — MySQL 8, base de données `rayhan_erp_db` - **rayhan-mysql** — MySQL 8, base de données `rayhan_erp_db`
- **rayhan-backend** — Spring Boot, accessible sur le port **8090** - **rayhan-backend** — Spring Boot, accessible sur le port **8090**
@ -254,8 +254,8 @@ ENTRYPOINT ["java", "-jar", "app.jar"]
| Service | URL | | Service | URL |
|---------|-----| |---------|-----|
| API REST | http://192.168.100.33:8090 | | API REST | https://rayhan-erp.bolbol.tn |
| Documentation Swagger UI | http://192.168.100.33:8090/swagger-ui/index.html | | Documentation Swagger UI | https://rayhan-erp.bolbol.tn/swagger-ui/index.html |
| Dépôt source (Gitea) | https://gitea.bolbol.tn/bolbol/rayhan-erp | | Dépôt source (Gitea) | https://gitea.bolbol.tn/bolbol/rayhan-erp |
--- ---
@ -272,7 +272,7 @@ L'API intègre **Swagger UI** (SpringDoc OpenAPI 2.5.0), accessible depuis n'imp
**Procédure de test :** **Procédure de test :**
1. Ouvrir http://192.168.100.33:8090/swagger-ui/index.html 1. Ouvrir https://rayhan-erp.bolbol.tn/swagger-ui/index.html
2. Exécuter `POST /api/auth/signin` avec `{"username":"admin","password":"Rayhan2024!"}` 2. Exécuter `POST /api/auth/signin` avec `{"username":"admin","password":"Rayhan2024!"}`
3. Copier le token de la réponse 3. Copier le token de la réponse
4. Cliquer sur **Authorize 🔒** → coller le token → Authorize 4. Cliquer sur **Authorize 🔒** → coller le token → Authorize

View File

@ -17,6 +17,11 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.List;
@Configuration @Configuration
@EnableMethodSecurity @EnableMethodSecurity
@ -51,9 +56,26 @@ public class WebSecurityConfig {
return new BCryptPasswordEncoder(); return new BCryptPasswordEncoder();
} }
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration config = new CorsConfiguration();
config.setAllowedOriginPatterns(List.of(
"https://rayhan-erp.bolbol.tn",
"http://localhost:*",
"http://127.0.0.1:*"
));
config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
config.setAllowedHeaders(List.of("*"));
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);
return source;
}
@Bean @Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http http
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
.csrf(AbstractHttpConfigurer::disable) .csrf(AbstractHttpConfigurer::disable)
.exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler)) .exceptionHandling(exception -> exception.authenticationEntryPoint(unauthorizedHandler))
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))

View File

@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController @RestController
@RequestMapping("/api/articles") @RequestMapping("/api/articles")
public class ArticleController { public class ArticleController {

View File

@ -26,7 +26,6 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController @RestController
@RequestMapping("/api/auth") @RequestMapping("/api/auth")
public class AuthController { public class AuthController {

View File

@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController @RestController
@RequestMapping("/api/clients") @RequestMapping("/api/clients")
public class ClientController { public class ClientController {

View File

@ -15,7 +15,6 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController @RestController
@RequestMapping("/api/dashboard") @RequestMapping("/api/dashboard")
public class DashboardController { public class DashboardController {

View File

@ -10,7 +10,6 @@ import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController @RestController
@RequestMapping("/api/fournisseurs") @RequestMapping("/api/fournisseurs")
public class FournisseurController { public class FournisseurController {

View File

@ -18,7 +18,6 @@ import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController @RestController
@RequestMapping("/api/production") @RequestMapping("/api/production")
public class ProductionOrderController { public class ProductionOrderController {

View File

@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController @RestController
@RequestMapping("/api/purchase-orders") @RequestMapping("/api/purchase-orders")
public class PurchaseOrderController { public class PurchaseOrderController {

View File

@ -14,7 +14,6 @@ import org.springframework.web.bind.annotation.*;
import java.util.List; import java.util.List;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController @RestController
@RequestMapping("/api/sales-orders") @RequestMapping("/api/sales-orders")
public class SalesOrderController { public class SalesOrderController {

View File

@ -17,7 +17,6 @@ import java.math.BigDecimal;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController @RestController
@RequestMapping("/api/stock") @RequestMapping("/api/stock")
public class StockController { public class StockController {

View File

@ -6,6 +6,11 @@
# Serveur # Serveur
server.port=8080 server.port=8080
# Reverse proxy HTTPS (Nginx/Traefik devant le conteneur)
server.forward-headers-strategy=framework
server.tomcat.remoteip.remote-ip-header=x-forwarded-for
server.tomcat.remoteip.protocol-header=x-forwarded-proto
# Base de données MySQL # Base de données MySQL
spring.datasource.url=jdbc:mysql://localhost:3306/rayhan_erp_db?createDatabaseIfNotExist=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Africa/Tunis spring.datasource.url=jdbc:mysql://localhost:3306/rayhan_erp_db?createDatabaseIfNotExist=true&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Africa/Tunis
spring.datasource.username=root spring.datasource.username=root