From efa54950a866ac3b8f57895a90d2d9a37964ffbe Mon Sep 17 00:00:00 2001
From: Lunga Tsewu <1998tsewu@gmail.com>
Date: Thu, 18 Jul 2024 13:38:48 +0200
Subject: [PATCH] Added oauth2 using keycloak for authentication and
authorization
---
.../main/resources/config/gatewayserver.yml | 6 +++
docker-compose-servers.yml | 15 +++++++
gateway-server/pom.xml | 17 ++++++++
.../config/KeycloakRoleConverter.java | 31 ++++++++++++++
.../java/com/gatewayserver/config/Roles.java | 6 +++
.../gatewayserver/config/SecurityConfig.java | 41 +++++++++++++++++++
.../src/main/resources/application.yml | 7 ++++
7 files changed, 123 insertions(+)
create mode 100644 gateway-server/src/main/java/com/gatewayserver/config/KeycloakRoleConverter.java
create mode 100644 gateway-server/src/main/java/com/gatewayserver/config/Roles.java
create mode 100644 gateway-server/src/main/java/com/gatewayserver/config/SecurityConfig.java
diff --git a/config-server/src/main/resources/config/gatewayserver.yml b/config-server/src/main/resources/config/gatewayserver.yml
index 3b5903c..3732ff3 100644
--- a/config-server/src/main/resources/config/gatewayserver.yml
+++ b/config-server/src/main/resources/config/gatewayserver.yml
@@ -1,5 +1,11 @@
server:
port: 8072
+spring:
+ security:
+ oauth2:
+ resourceserver:
+ jwt:
+ jwk-set-uri: "http://keyClock:8080/realms/master/protocol/openid-connect/certs"
resilience4j.circuitbreaker:
configs:
default:
diff --git a/docker-compose-servers.yml b/docker-compose-servers.yml
index 94116fd..acff9ab 100644
--- a/docker-compose-servers.yml
+++ b/docker-compose-servers.yml
@@ -86,6 +86,21 @@ services:
- discoveryServerApp
networks:
- server_net
+
+ #KEYCLOCK CONTAINER
+ keyClock:
+ image: quay.io/keycloak/keycloak:23.0.6
+ container_name: keyClock
+ hostname: keyClock
+ ports:
+ - "7080:8080"
+ networks:
+ - server_net
+ environment:
+ - KEYCLOAK_ADMIN=admin
+ - KEYCLOAK_ADMIN_PASSWORD=admin
+ command:
+ - start-dev
networks:
server_net:
external: true
diff --git a/gateway-server/pom.xml b/gateway-server/pom.xml
index 355aaad..c8058f9 100644
--- a/gateway-server/pom.xml
+++ b/gateway-server/pom.xml
@@ -50,6 +50,23 @@
spring-boot-starter-test
test
+
+ org.springframework.boot
+ spring-boot-starter-security
+
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-resource-server
+
+
+ org.springframework.security
+ spring-security-oauth2-jose
+
+
+ org.springframework.security
+ spring-security-test
+ test
+
diff --git a/gateway-server/src/main/java/com/gatewayserver/config/KeycloakRoleConverter.java b/gateway-server/src/main/java/com/gatewayserver/config/KeycloakRoleConverter.java
new file mode 100644
index 0000000..537c933
--- /dev/null
+++ b/gateway-server/src/main/java/com/gatewayserver/config/KeycloakRoleConverter.java
@@ -0,0 +1,31 @@
+package com.gatewayserver.config;
+
+import org.springframework.core.convert.converter.Converter;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.jwt.Jwt;
+
+import java.util.ArrayList;
+import java.util.Collection;
+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");
+ if(realmAccess==null||realmAccess.isEmpty()){
+ return new ArrayList<>();
+ }
+ return realmAccess.get("roles")
+ .stream().map(roleName->new SimpleGrantedAuthority("ROLE_"+roleName))
+ .collect(Collectors.toSet());
+
+ }
+
+ /*@Override
+ public Converter andThen(Converter super Collection, ? extends U> after) {
+ return Converter.super.andThen(after);
+ }*/
+}
diff --git a/gateway-server/src/main/java/com/gatewayserver/config/Roles.java b/gateway-server/src/main/java/com/gatewayserver/config/Roles.java
new file mode 100644
index 0000000..1307bc9
--- /dev/null
+++ b/gateway-server/src/main/java/com/gatewayserver/config/Roles.java
@@ -0,0 +1,6 @@
+package com.gatewayserver.config;
+
+public enum Roles {
+ CLIENT,
+ ADMIN
+}
diff --git a/gateway-server/src/main/java/com/gatewayserver/config/SecurityConfig.java b/gateway-server/src/main/java/com/gatewayserver/config/SecurityConfig.java
new file mode 100644
index 0000000..13bced9
--- /dev/null
+++ b/gateway-server/src/main/java/com/gatewayserver/config/SecurityConfig.java
@@ -0,0 +1,41 @@
+package com.gatewayserver.config;
+
+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;
+import org.springframework.security.oauth2.jwt.Jwt;
+import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
+import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
+import org.springframework.security.web.server.SecurityWebFilterChain;
+import reactor.core.publisher.Mono;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig {
+ private final String[] PATH_TO_AUTHENTICATE={"pdf-editor/users/**","pdf-editor/documents/**","pdf-editor/emails/**"};
+
+ @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())
+
+
+ )
+ .oauth2ResourceServer(oauth2Server->oauth2Server.jwt(jwtSpec ->
+ jwtSpec.jwtAuthenticationConverter(grantedAuth())));
+ httpSecurity.csrf(ServerHttpSecurity.CsrfSpec::disable);
+ return httpSecurity.build();
+ }
+
+ private Converter> grantedAuth() {
+ var jwtConv= new JwtAuthenticationConverter();
+ jwtConv.setJwtGrantedAuthoritiesConverter(new KeycloakRoleConverter());
+ return new ReactiveJwtAuthenticationConverterAdapter(jwtConv);
+ }
+}
diff --git a/gateway-server/src/main/resources/application.yml b/gateway-server/src/main/resources/application.yml
index 9ed31ee..a9b1098 100644
--- a/gateway-server/src/main/resources/application.yml
+++ b/gateway-server/src/main/resources/application.yml
@@ -26,6 +26,13 @@ spring:
httpclient:
connect-timeout: 5000
response-timeout: 5s
+ security:
+ oauth2:
+ resourceserver:
+ jwt:
+ jwk-set-uri: "http://keyClock:8080/realms/master/protocol/openid-connect/certs"
+ main:
+ allow-bean-definition-overriding: true
management:
endpoints:
web: