diff --git a/hello-service/build.gradle b/hello-service/build.gradle
index 56520cc..cbd7825 100644
--- a/hello-service/build.gradle
+++ b/hello-service/build.gradle
@@ -1,6 +1,6 @@
plugins {
id 'java'
- id 'org.springframework.boot' version '3.0.1'
+ id 'org.springframework.boot' version '3.1.0'
id 'io.spring.dependency-management' version '1.1.0'
}
@@ -18,7 +18,7 @@ repositories {
}
ext {
- set('springCloudVersion', "2022.0.0")
+ set('springCloudVersion', "2022.0.3")
}
dependencies {
diff --git a/hello-service/gradle/wrapper/gradle-wrapper.properties b/hello-service/gradle/wrapper/gradle-wrapper.properties
index 070cb70..aba097b 100644
--- a/hello-service/gradle/wrapper/gradle-wrapper.properties
+++ b/hello-service/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
+zipStorePath=wrapper/dists
\ No newline at end of file
diff --git a/helm/spring-cloud-kubernetes-demo/templates/ui-interface.yml b/helm/spring-cloud-kubernetes-demo/templates/ui-interface.yml
index e746919..bb291c8 100644
--- a/helm/spring-cloud-kubernetes-demo/templates/ui-interface.yml
+++ b/helm/spring-cloud-kubernetes-demo/templates/ui-interface.yml
@@ -5,7 +5,7 @@ metadata:
namespace: {{ .Release.Namespace }}
data:
application.yaml: |-
- testValue: it is a test value
+ postLogoutUrl: {{ .Values.ui.postLogoutUrl }}
server:
forward-headers-strategy: framework
@@ -14,6 +14,24 @@ data:
redis:
host: {{ .Release.Namespace }}-redis-master
+
+ security:
+ oauth2:
+ client:
+ registration:
+ client:
+ client-id: {{ .Values.keycloak.sso.clientId }}
+ client-secret: {{ .Values.keycloak.sso.clientSecret }}
+ client-name: keycloak
+ provider: keycloak
+ scope:
+ - openid
+ authorization-grant-type: authorization_code
+ provider:
+ keycloak:
+ issuer-uri: {{ .Values.keycloak.tenantUrl }}
+ user-name-attribute: preferred_username
+
cloud:
gateway:
routes:
diff --git a/helm/spring-cloud-kubernetes-demo/values.yaml b/helm/spring-cloud-kubernetes-demo/values.yaml
index fb24731..bd3d95d 100644
--- a/helm/spring-cloud-kubernetes-demo/values.yaml
+++ b/helm/spring-cloud-kubernetes-demo/values.yaml
@@ -14,7 +14,14 @@ helloService:
pullPolicy: Always
tag: latest
+keycloak:
+ tenantUrl: http://keycloak.local
+ sso:
+ clientId: xxx
+ clientSecret: xxx
+
ui:
+ postLogoutUrl: http://localhost:8080
replicas: 2
image:
pullPolicy: Always
diff --git a/message-service/build.gradle.kts b/message-service/build.gradle.kts
index 3a184b8..063033d 100644
--- a/message-service/build.gradle.kts
+++ b/message-service/build.gradle.kts
@@ -1,10 +1,10 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
- id("org.springframework.boot") version "3.0.1"
+ id("org.springframework.boot") version "3.1.0"
id("io.spring.dependency-management") version "1.1.0"
- kotlin("jvm") version "1.7.22"
- kotlin("plugin.spring") version "1.7.22"
+ kotlin("jvm") version "1.8.21"
+ kotlin("plugin.spring") version "1.8.21"
}
group = "it.valeriovaudi"
@@ -14,7 +14,7 @@ repositories {
mavenCentral()
}
-extra["springCloudVersion"] = "2022.0.0"
+extra["springCloudVersion"] = "2022.0.3"
dependencies {
implementation("org.springframework.cloud:spring-cloud-starter-bootstrap")
diff --git a/message-service/gradle/wrapper/gradle-wrapper.properties b/message-service/gradle/wrapper/gradle-wrapper.properties
index 070cb70..aba097b 100644
--- a/message-service/gradle/wrapper/gradle-wrapper.properties
+++ b/message-service/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
+zipStorePath=wrapper/dists
\ No newline at end of file
diff --git a/ui/pom.xml b/ui/pom.xml
index c77eb74..8e7fed6 100644
--- a/ui/pom.xml
+++ b/ui/pom.xml
@@ -5,7 +5,7 @@
org.springframework.boot
spring-boot-starter-parent
- 3.0.1
+ 3.1.0
it.valeriovaudi
@@ -16,10 +16,14 @@
17
- 2022.0.0
+ 2022.0.3
+
+ org.springframework.boot
+ spring-boot-starter-oauth2-client
+
org.springframework.cloud
spring-cloud-starter-bootstrap
diff --git a/ui/src/main/java/it/valeriovaudi/ui/UiApplication.java b/ui/src/main/java/it/valeriovaudi/ui/UiApplication.java
index 91d80f9..a6f0ba5 100644
--- a/ui/src/main/java/it/valeriovaudi/ui/UiApplication.java
+++ b/ui/src/main/java/it/valeriovaudi/ui/UiApplication.java
@@ -1,16 +1,29 @@
package it.valeriovaudi.ui;
+import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
-import org.springframework.security.core.userdetails.MapReactiveUserDetailsService;
-import org.springframework.security.core.userdetails.User;
-import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.authority.SimpleGrantedAuthority;
+import org.springframework.security.oauth2.client.oidc.userinfo.OidcReactiveOAuth2UserService;
+import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
+import org.springframework.security.oauth2.client.oidc.web.server.logout.OidcClientInitiatedServerLogoutSuccessHandler;
+import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository;
+import org.springframework.security.oauth2.client.userinfo.ReactiveOAuth2UserService;
+import org.springframework.security.oauth2.core.oidc.user.DefaultOidcUser;
+import org.springframework.security.oauth2.core.oidc.user.OidcUser;
+import org.springframework.security.oauth2.core.oidc.user.OidcUserAuthority;
import org.springframework.security.web.server.SecurityWebFilterChain;
+import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler;
+import reactor.core.publisher.Mono;
-import static java.util.Arrays.asList;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
@SpringBootApplication
public class UiApplication {
@@ -21,34 +34,67 @@ public static void main(String[] args) {
}
+@EnableWebFluxSecurity
@Configuration(proxyBeanMethods = false)
class SecurityConfig {
+ private final ReactiveClientRegistrationRepository clientRegistrationRepository;
+
+ SecurityConfig(ReactiveClientRegistrationRepository clientRegistrationRepository) {
+ this.clientRegistrationRepository = clientRegistrationRepository;
+ }
+
@Bean
- public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
- return http.csrf().disable().authorizeExchange()
- .pathMatchers("/index.html").hasRole("USER")
- .pathMatchers("/messages.html").hasRole("ADMIN")
- .anyExchange().permitAll()
- .and().formLogin()
- .and().logout()
- .and().build();
+ public ReactiveOAuth2UserService oidcUserService() {
+ final OidcReactiveOAuth2UserService delegate = new OidcReactiveOAuth2UserService();
+
+
+ return (userRequest) -> {
+ // Delegate to the default implementation for loading a user
+ return delegate.loadUser(userRequest)
+ .flatMap((oidcUser) -> {
+ List authorities = (List) oidcUser.getClaimAsMap("realm_access").get("roles");
+ Set oidcAuthorities = authorities.stream()
+ .map(SimpleGrantedAuthority::new)
+ .map(authority -> new OidcUserAuthority(authority.getAuthority(), oidcUser.getIdToken(), oidcUser.getUserInfo()))
+ .collect(Collectors.toSet());
+ return Mono.just(new DefaultOidcUser(oidcAuthorities, oidcUser.getIdToken(), oidcUser.getUserInfo()));
+ });
+ };
+
}
@Bean
- public MapReactiveUserDetailsService userDetailsService() {
- UserDetails user = User.builder()
- .username("user")
- .password("{noop}secret")
- .roles("USER")
- .build();
-
- UserDetails admin = User.builder()
- .username("admin")
- .password("{noop}secret")
- .roles("ADMIN")
- .build();
-
- return new MapReactiveUserDetailsService(asList(admin, user));
+ public SecurityWebFilterChain defaultSecurityFilterChain(
+ @Value("${postLogoutUrl}") String postLogoutUrl,
+ ServerHttpSecurity http) {
+ http.csrf(ServerHttpSecurity.CsrfSpec::disable);
+ http.headers(configurer -> configurer.frameOptions(ServerHttpSecurity.HeaderSpec.FrameOptionsSpec::disable));
+
+ http.oauth2Login(Customizer.withDefaults());
+ http.logout(logoutSpec -> {
+ logoutSpec.logoutSuccessHandler(oidcLogoutSuccessHandler(postLogoutUrl, clientRegistrationRepository));
+ });
+
+ http.authorizeExchange(
+ auth ->
+ auth
+ .pathMatchers("/").hasAuthority("USER")
+ .pathMatchers("/index.html").hasAuthority("USER")
+ .pathMatchers("/messages.html").hasAuthority("ADMIN")
+ .anyExchange().permitAll()
+ );
+
+ return http.build();
}
-}
\ No newline at end of file
+
+ private ServerLogoutSuccessHandler oidcLogoutSuccessHandler(
+ String postLogoutUrl,
+ ReactiveClientRegistrationRepository clientRegistrationRepository
+ ) {
+ OidcClientInitiatedServerLogoutSuccessHandler oidcLogoutSuccessHandler =
+ new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository);
+ oidcLogoutSuccessHandler.setPostLogoutRedirectUri(postLogoutUrl);
+ return oidcLogoutSuccessHandler;
+ }
+}