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, ? 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: