From 1a0e404249469d1cad11ca0092b25801a9b44390 Mon Sep 17 00:00:00 2001
From: Lunga Tsewu <1998tsewu@gmail.com>
Date: Thu, 18 Jul 2024 21:12:04 +0200
Subject: [PATCH] Added security for admin roles and access between servers
through docker network
---
config-server/pom.xml | 4 ++
.../configserver/ConfigServerApplication.java | 2 +
.../src/main/resources/application.yml | 18 ++++--
.../main/resources/config/discoveryserver.yml | 4 +-
.../main/resources/config/gatewayserver.yml | 14 ++---
.../src/main/resources/application.yml | 4 +-
docker-compose-servers.yml | 10 ---
.../src/main/resources/application.yml | 9 +--
.../src/main/resources/application.yml | 9 +--
.../config/KeycloakRoleConverter.java | 6 +-
.../gatewayserver/config/RoutesConfig.java | 63 ++++++++++++-------
.../gatewayserver/config/SecurityConfig.java | 15 +++--
.../src/main/resources/application.yml | 8 ++-
.../src/main/resources/application.yml | 9 +--
14 files changed, 97 insertions(+), 78 deletions(-)
diff --git a/config-server/pom.xml b/config-server/pom.xml
index d213487..9670e15 100644
--- a/config-server/pom.xml
+++ b/config-server/pom.xml
@@ -20,6 +20,10 @@
org.springframework.cloud
spring-cloud-config-monitor
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
org.springframework.boot
spring-boot-starter-actuator
diff --git a/config-server/src/main/java/com/configserver/ConfigServerApplication.java b/config-server/src/main/java/com/configserver/ConfigServerApplication.java
index 5db4c8a..3cd9bd0 100644
--- a/config-server/src/main/java/com/configserver/ConfigServerApplication.java
+++ b/config-server/src/main/java/com/configserver/ConfigServerApplication.java
@@ -2,10 +2,12 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;
@SpringBootApplication
@EnableConfigServer
+@EnableDiscoveryClient
public class ConfigServerApplication {
public static void main(String[] args) {
diff --git a/config-server/src/main/resources/application.yml b/config-server/src/main/resources/application.yml
index 64a8b53..c8a8eaa 100644
--- a/config-server/src/main/resources/application.yml
+++ b/config-server/src/main/resources/application.yml
@@ -47,10 +47,20 @@ spring:
properties:
topic:
replication-factor: 1
-
-
-server:
- port: 8071
+eureka:
+ instance:
+ hostname: discoveryServerApp
+ preferIpAddress: true
+ client:
+ fetch-registry: true
+ register-with-eureka: true
+ serviceUrl:
+ defaultZone: http://${eureka.instance.hostname}:8080/eureka/
+ healthcheck:
+ enabled: true
+endpoints:
+ shutdown:
+ enabled: true
encrypt:
key: 7E93D539EAA5C42F8523AB1627D1B
management:
diff --git a/config-server/src/main/resources/config/discoveryserver.yml b/config-server/src/main/resources/config/discoveryserver.yml
index 33fb4e2..43dfe0e 100644
--- a/config-server/src/main/resources/config/discoveryserver.yml
+++ b/config-server/src/main/resources/config/discoveryserver.yml
@@ -1,10 +1,10 @@
server:
- port: 8070
+ port: 8080
eureka:
instance:
hostname: discoveryServerApp
client:
fetch-registry: false
- register-with-eureka: false
+ register-with-eureka: true
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
\ No newline at end of file
diff --git a/config-server/src/main/resources/config/gatewayserver.yml b/config-server/src/main/resources/config/gatewayserver.yml
index 3732ff3..5cd7cd0 100644
--- a/config-server/src/main/resources/config/gatewayserver.yml
+++ b/config-server/src/main/resources/config/gatewayserver.yml
@@ -1,11 +1,9 @@
-server:
- port: 8072
-spring:
- security:
- oauth2:
- resourceserver:
- jwt:
- jwk-set-uri: "http://keyClock:8080/realms/master/protocol/openid-connect/certs"
+#spring:
+# security:
+# oauth2:
+# resourceserver:
+# jwt:
+# jwk-set-uri: "http://keyClock:8080/realms/master/protocol/openid-connect/certs"
resilience4j.circuitbreaker:
configs:
default:
diff --git a/discovery-server/src/main/resources/application.yml b/discovery-server/src/main/resources/application.yml
index 2deb711..c45965c 100644
--- a/discovery-server/src/main/resources/application.yml
+++ b/discovery-server/src/main/resources/application.yml
@@ -2,11 +2,11 @@ spring:
application:
name: discoveryserver
config:
- import: "configserver:http://configServerApp:8071/"
+ import: "configserver:http://configServerApp:8080/"
cloud:
config:
enabled: true
- uri: "http://configServerApp:8071"
+ uri: "http://configServerApp:8080"
stream:
kafka:
binder:
diff --git a/docker-compose-servers.yml b/docker-compose-servers.yml
index acff9ab..bffd18c 100644
--- a/docker-compose-servers.yml
+++ b/docker-compose-servers.yml
@@ -9,8 +9,6 @@ services:
context: ./config-server
image: pdf_editor_config:latest
container_name: configServerApp
- ports:
- - "8071:8071"
networks:
- server_net
@@ -21,8 +19,6 @@ services:
context: ./discovery-server
image: pdf_editor_discover:latest
container_name: discoveryServerApp
- ports:
- - "8070:8070"
depends_on:
- configServerApp
networks:
@@ -48,8 +44,6 @@ services:
context: ./document-service
image: pdf-editor-document:latest
container_name: documentsServerApp
- ports:
- - "8082:8082"
depends_on:
- configServerApp
- discoveryServerApp
@@ -64,8 +58,6 @@ services:
context: ./email-service
image: pdf-editor-email:latest
container_name: emailServerApp
- ports:
- - "8083:8083"
depends_on:
- configServerApp
- discoveryServerApp
@@ -79,8 +71,6 @@ services:
image: pdf-editor-user:latest
container_name: usersServerApp
hostname: usersServerApp
- ports:
- - "8080:8080"
depends_on:
- configServerApp
- discoveryServerApp
diff --git a/document-service/src/main/resources/application.yml b/document-service/src/main/resources/application.yml
index edf7334..de84986 100644
--- a/document-service/src/main/resources/application.yml
+++ b/document-service/src/main/resources/application.yml
@@ -2,11 +2,11 @@ spring:
application:
name: "documents"
config:
- import: "configserver:http://configServerApp:8071/"
+ import: "configserver:http://configServerApp:8080/"
cloud:
config:
enabled: true
- uri: "http://configServerApp:8071"
+ uri: "http://configServerApp:8080"
stream:
kafka:
binder:
@@ -29,9 +29,6 @@ spring:
topics:
download-document-name: download-document-event
-
-server:
- port: 8082
management:
endpoints:
web:
@@ -52,7 +49,7 @@ eureka:
fetch-registry: true
register-with-eureka: true
serviceUrl:
- defaultZone: http://${eureka.instance.hostname}:8070/eureka/
+ defaultZone: http://${eureka.instance.hostname}:8080/eureka/
healthcheck:
enabled: true
endpoints:
diff --git a/email-service/src/main/resources/application.yml b/email-service/src/main/resources/application.yml
index 30dbe72..408453f 100644
--- a/email-service/src/main/resources/application.yml
+++ b/email-service/src/main/resources/application.yml
@@ -7,14 +7,14 @@ spring:
application:
name: "email"
config:
- import: "configserver:http://configServerApp:8071/"
+ import: "configserver:http://configServerApp:8080/"
jackson:
parser:
allow-unquoted-field-names: true
cloud:
config:
enabled: true
- uri: http://configServerApp:8071
+ uri: http://configServerApp:8080
stream:
kafka:
binder:
@@ -30,9 +30,6 @@ spring:
topics:
retry: "download-document-event.RETRY"
dead: "download-document-event.DEAD"
-
-server:
- port: 8083
management:
endpoints:
web:
@@ -53,7 +50,7 @@ eureka:
fetch-registry: true
register-with-eureka: true
serviceUrl:
- defaultZone: http://${eureka.instance.hostname}:8070/eureka/
+ defaultZone: http://${eureka.instance.hostname}:8080/eureka/
healthcheck:
enabled: true
endpoints:
diff --git a/gateway-server/src/main/java/com/gatewayserver/config/KeycloakRoleConverter.java b/gateway-server/src/main/java/com/gatewayserver/config/KeycloakRoleConverter.java
index 537c933..e529d20 100644
--- a/gateway-server/src/main/java/com/gatewayserver/config/KeycloakRoleConverter.java
+++ b/gateway-server/src/main/java/com/gatewayserver/config/KeycloakRoleConverter.java
@@ -7,20 +7,20 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
-import java.util.Set;
import java.util.stream.Collectors;
public class KeycloakRoleConverter implements Converter> {
@Override
public Collection convert(Jwt source) {
- Map> realmAccess = (Map>) source.getClaims().get("realm_access");
+ Map> realmAccess = (Map>) source.getClaims().get("realm_access");
if(realmAccess==null||realmAccess.isEmpty()){
return new ArrayList<>();
}
return realmAccess.get("roles")
.stream().map(roleName->new SimpleGrantedAuthority("ROLE_"+roleName))
- .collect(Collectors.toSet());
+ .collect(Collectors.toList());
}
diff --git a/gateway-server/src/main/java/com/gatewayserver/config/RoutesConfig.java b/gateway-server/src/main/java/com/gatewayserver/config/RoutesConfig.java
index 5bad45d..4badf8d 100644
--- a/gateway-server/src/main/java/com/gatewayserver/config/RoutesConfig.java
+++ b/gateway-server/src/main/java/com/gatewayserver/config/RoutesConfig.java
@@ -4,51 +4,68 @@
import org.springframework.cloud.circuitbreaker.resilience4j.ReactiveResilience4JCircuitBreakerFactory;
import org.springframework.cloud.circuitbreaker.resilience4j.Resilience4JConfigBuilder;
import org.springframework.cloud.client.circuitbreaker.Customizer;
+import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.route.RouteLocator;
+import org.springframework.cloud.gateway.route.builder.Buildable;
+import org.springframework.cloud.gateway.route.builder.PredicateSpec;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import java.time.Duration;
+import java.util.function.Function;
@Configuration
public class RoutesConfig {
+
+
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes().
route(predicateSpec -> predicateSpec.path("/pdf-editor/users/**").
filters(filterSpec->
- {
-
- return filterSpec.rewritePath("/pdf-editor/users/(?.*)", "/pdf-editor/users/${segment}")
+ filterSpec.rewritePath("/pdf-editor/users/(?.*)", "/pdf-editor/users/${segment}")
.circuitBreaker(circuitConfi ->
circuitConfi.setName("userServiceCircuitBreaker")
.setFallbackUri("/pdf-editor/fallback/contact-team")
- );
-
- })
- .uri("lb://USERS"))
- .route(predicateSpec -> predicateSpec.path("/pdf-editor/documents/**").
- filters(filterSpec->filterSpec.rewritePath("/pdf-editor/documents/(?.*)","/pdf-editor/documents/${segment}")
- .circuitBreaker(circuitConfi->
- circuitConfi.setName("documentsCircuitBreaker")
- .setFallbackUri("/pdf-editor/fallback/contact-team")
- .addStatusCode(HttpStatus.SERVICE_UNAVAILABLE.name())
- ))
- .uri("lb://DOCUMENTS"))
- .route(predicateSpec -> predicateSpec.path("/pdf-editor/emails/**").
- filters(filterSpec->filterSpec.rewritePath("/pdf-editor/emails/(?.*)","/pdf-editor/email/${segment}")
- .circuitBreaker(circuitConfi->
- circuitConfi.setName("emailCircuitBreaker")
- .setFallbackUri("/pdf-editor/fallback/contact-team")
- .addStatusCode(HttpStatus.SERVICE_UNAVAILABLE.name())
- ))
- .uri("lb://EMAIL"))
+ )
+ ).uri("lb://USERS"))
+ .route(
+ buildRoute("/pdf-editor/documents/**","/pdf-editor/documents/(?.*)",
+ "/pdf-editor/documents/${segment}","documentsCircuitBreaker"
+ ,"/pdf-editor/fallback/contact-team","lb://DOCUMENTS")
+ )
+ .route(buildRoute("/pdf-editor/emails/**","/pdf-editor/emails/(?.*)",
+ "/pdf-editor/emails/${segment}","emailCircuitBreaker"
+ ,"/pdf-editor/fallback/contact-team","lb://EMAILS")
+
+ )
+ .route(buildRoute("/pdf-editor/configserver/**","/pdf-editor/configserver/(?.*)",
+ "/${segment}","configServerCircuitBreaker"
+ ,"/pdf-editor/fallback/contact-team","lb://CONFIGSERVER")
+
+ )
+ .route(buildRoute("/pdf-editor/discoveryserver/**","/pdf-editor/discoveryserver/(?.*)",
+ "/${segment}","discoveryServerCircuitBreaker"
+ ,"/pdf-editor/fallback/contact-team","lb://DISCOVERYSERVER")
+
+ )
.build();
}
+ private Function> buildRoute(String path, String rewritePath,String replacementPath, String circuitBreakerName, String fallbackUri,
+ String loadBalanceUri){
+ return predicateSpec -> predicateSpec.path(path).
+ filters(filterSpec->filterSpec.rewritePath(rewritePath,replacementPath)
+ .circuitBreaker(circuitConfi->
+ circuitConfi.setName(circuitBreakerName)
+ .setFallbackUri(fallbackUri)
+ .addStatusCode(HttpStatus.SERVICE_UNAVAILABLE.name())
+ ))
+ .uri(loadBalanceUri);
+ }
@Bean
public Customizer circuitBreakerFactoryCustomizer(){
return factory->factory.configureDefault(
diff --git a/gateway-server/src/main/java/com/gatewayserver/config/SecurityConfig.java b/gateway-server/src/main/java/com/gatewayserver/config/SecurityConfig.java
index 13bced9..8ef94ae 100644
--- a/gateway-server/src/main/java/com/gatewayserver/config/SecurityConfig.java
+++ b/gateway-server/src/main/java/com/gatewayserver/config/SecurityConfig.java
@@ -3,7 +3,6 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
-import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
@@ -13,17 +12,23 @@
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;
+import static com.gatewayserver.config.Roles.ADMIN;
+import static com.gatewayserver.config.Roles.CLIENT;
+
@Configuration
@EnableWebSecurity
public class SecurityConfig {
- private final String[] PATH_TO_AUTHENTICATE={"pdf-editor/users/**","pdf-editor/documents/**","pdf-editor/emails/**"};
+ private final String[] USER_PATH_TO_AUTHENTICATE={"pdf-editor/users/**","pdf-editor/documents/**","pdf-editor/emails/**"};
+ private final String[] ADMIN_PATH_TO_AUTHENTICATE={"/pdf-editor/discoveryserver/**","/pdf-editor/configserver/**"};
+
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity httpSecurity){
httpSecurity.
- authorizeExchange(exchange->
- exchange.pathMatchers(HttpMethod.GET).permitAll()
- .pathMatchers(PATH_TO_AUTHENTICATE).hasAnyRole(Roles.ADMIN.name(),Roles.CLIENT.name())
+ authorizeExchange(exchange->exchange
+ //exchange.pathMatchers(HttpMethod.GET).permitAll()
+ .pathMatchers(USER_PATH_TO_AUTHENTICATE).hasAnyRole(ADMIN.name(),CLIENT.name())
+ .pathMatchers(ADMIN_PATH_TO_AUTHENTICATE).hasRole(ADMIN.name())
)
diff --git a/gateway-server/src/main/resources/application.yml b/gateway-server/src/main/resources/application.yml
index a9b1098..4a1fd36 100644
--- a/gateway-server/src/main/resources/application.yml
+++ b/gateway-server/src/main/resources/application.yml
@@ -1,12 +1,14 @@
+server:
+ port: 8072
spring:
application:
name: "gatewayserver"
config:
- import: "configserver:http://configServerApp:8071/"
+ import: "configserver:http://configServerApp:8080/"
cloud:
config:
enabled: true
- uri: http://configServerApp:8071
+ uri: http://configServerApp:8080
stream:
kafka:
binder:
@@ -52,7 +54,7 @@ eureka:
fetch-registry: true
register-with-eureka: true
serviceUrl:
- defaultZone: http://${eureka.instance.hostname}:8070/eureka/
+ defaultZone: http://${eureka.instance.hostname}:8080/eureka/
healthcheck:
enabled: true
logging:
diff --git a/user-service/src/main/resources/application.yml b/user-service/src/main/resources/application.yml
index 1c97540..0301df9 100644
--- a/user-service/src/main/resources/application.yml
+++ b/user-service/src/main/resources/application.yml
@@ -2,11 +2,11 @@ spring:
application:
name: "users"
config:
- import: "configserver:http://configServerApp:8071/"
+ import: "configserver:http://configServerApp:8080/"
cloud:
config:
enabled: true
- uri: http://configServerApp:8071
+ uri: http://configServerApp:8080
stream:
kafka:
binder:
@@ -19,9 +19,6 @@ spring:
env:
enabled: true
-server:
- port: 8080
-
management:
endpoints:
web:
@@ -41,7 +38,7 @@ eureka:
fetch-registry: true
register-with-eureka: true
serviceUrl:
- defaultZone: http://${eureka.instance.hostname}:8070/eureka/
+ defaultZone: http://${eureka.instance.hostname}:8080/eureka/
healthcheck:
enabled: true
endpoints: