How to programmatically configure keycloak with spring cloud gateway api? - java

I am building one spring cloud gateway and in that I am implementing Keycloak Security everything works fine but need to do programmatically instead of writing in yml file there can be multiple Realms.
Below are the dependencies which I am using:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
And my application Api gateway start application code is as below:
#SpringBootApplication
#ComponentScan(basePackages = {"com", "com.b","com.auth","com.security"})
public class APIGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(APIGatewayApplication.class, args);
}
#Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new PathBasedKeycloakConfigResolver();
}
}
Security Config http code is as below:
#Configuration
public class SecurityConfig {
#Bean
public SecurityWebFilterChain springSecurityFilterChain (ServerHttpSecurity http) {
http
.authorizeExchange()
.anyExchange()
.authenticated()
.and()
.oauth2Login(); // to redirect to oauth2 login page.
return http.build();
}
}
and in my application.yml file I am adding below configuration:
spring:
security:
oauth2:
client:
provider:
my-keycloak-provider:
issuer-uri: http://localhost:8280/auth/realms/Default
registration:
keycloak-spring-gateway-client:
provider: my-keycloak-provider
client-id: Default
client-secret: 8ZRUH62Pfhfde6uqasD8dfgdhvqWt03K6
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/app'
main:
web-application-type: reactive
application:
name: app
cloud:
gateway:
default-filters:
- TokenRelay
So we can in configuration file I am manually adding configs for one Realms but there can be multiple realms in that how to do it programmatically dynamic? .

Related

Getting "An error occurred while attempting to decode the Jwt: Couldn't retrieve remote JWK set: " error when trying to decode the jwt token

this is my Security configuration file for resource server
#Configuration
#EnableWebSecurity
public class SecureSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(STATELESS)
.and()
.authorizeRequests()
.anyRequest()
.permitAll()
.antMatchers("api/**")
.authenticated()
.and()
.oauth2ResourceServer()
.jwt();
}
}
and those are dependencies for spring boot application (resource server app.)
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-core</artifactId>
<version>5.5.3</version>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
here is the jwk-set-url in the config yml file, to which my application is connecting at start up
spring:
security:
oauth2:
resourceserver:
jwt:
jwk-set-uri: http://localhost:8083/.well-known/openid-configuration/jwks
And via postman now when I'm getting the token from auth server via postman and trying to request to my server, I'm getting this kind of error
Bearer error="invalid_token", error_description="An error occurred while attempting to decode the Jwt: Couldn't retrieve remote JWK set: org.springframework.web.client.HttpClientErrorException$NotFound: 404 null", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"
who has faced such kind of issue?
Change the "Hostname Verification" to "none" in SSL properties tab.

Keycloak + spring cloud gateway + authentication and authorization

I'm trying to use keycloak for authentication and also authorization in the spring cloud gateway itself. But I'm getting the below error.
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'springSecurityFilterChain' defined in class path resource [poc/apigateway/SecurityConfig.class]:
Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.security.web.server.SecurityWebFilterChain]:
Factory method 'springSecurityFilterChain' threw exception; nested exception is java.lang.IllegalArgumentException:
clientRegistrationRepository cannot be null
My application.properties
spring.application.name=api-gateway
spring.cloud.gateway.default-filters=TokenRelay
spring.cloud.gateway.routes[0].id=product-service
spring.cloud.gateway.routes[0].uri=http://localhost:8009
spring.cloud.gateway.routes[0].predicates[0]=Path=/api/product
spring.cloud.gateway.routes[1].id=order-service
spring.cloud.gateway.routes[1].uri=http://localhost:8008
spring.cloud.gateway.routes[1].predicates[0]=Path=/api/order
spring.security.oauth2.client.provider.keycloak.issuer-uri=http://localhost:8080/auth/realms/microservice-realm
spring.security.oauth2.client.registration.spring-cloud-gateway-client.client-id=spring-cloud-gateway-client
spring.security.oauth2.client.registration.spring-cloud-gateway-client.client-secret=d1b3670c-f1c3-480c-9cda-8e107aec7d5b
spring.security.oauth2.client.registration.spring-cloud-gateway-client.scope=openid, profile, roles
spring.security.oauth2.client.registration.spring-cloud-gateway-client.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.spring-cloud-gateway-client.redirect-uri=http://localhost:8005/login/oauth2/code/spring-cloud-gateway-client
spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8080/auth/realms/microservice-realm
keycloak.realm=microservice-realm
keycloak.resource=spring-cloud-gateway-client
keycloak.auth-server-url=http://localhost:8080/auth
keycloak.public-client=true
keycloak.securityConstraints[0].authRoles[0]=app-user
keycloak.securityConstraints[0].securityCollections[0].patterns[0]=/api/*
My build.gradle
plugins {
id 'org.springframework.boot' version '2.5.4'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'poc'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', "2020.0.3")
set('keycloakVersion', '4.8.3.Final')
}
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
// Keycloak
implementation 'org.keycloak:keycloak-spring-boot-starter'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
mavenBom "org.keycloak.bom:keycloak-adapter-bom:${keycloakVersion}"
}
}
test {
useJUnitPlatform()
}
My SecurityConfig.java
#Configuration
#EnableWebFluxSecurity
public class SecurityConfig {
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.authorizeExchange(exchanges -> exchanges.anyExchange().authenticated())
.oauth2Login(withDefaults());
http.csrf().disable();
return http.build();
}
}
The issue comes when the application is booting up. What I'm trying to do here is do the authentication and role bases authorization in the API gateway itself.
The Keycloak adapter doesn't work with WebFlux, only with MVC, so you should remove this dependency:
mavenBom "org.keycloak.bom:keycloak-adapter-bom:${keycloakVersion}"
You can use
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
and configure the Keycloak as the Oauth server:
spring:
security:
oauth2:
client:
registration:
keycloak:
provider: keycloak
client-id: web
client-secret: '{cipher}xxxx'
authorization-grant-type: authorization_code
redirect-uri: ${keycloak-client.server-url}/login/oauth2/code/keycloak
scope: openid
provider:
keycloak:
authorization-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/auth
token-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/token
user-info-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/userinfo
jwk-set-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/certs
user-name-attribute: name
user-info-authentication-method: header
resourceserver:
jwt:
jwk-set-uri: ${keycloak-client.server-url}/realms/${keycloak-client.realm}/protocol/openid-connect/certs
keycloak-client:
server-url: https://keycloakURL/auth
realm: your-realm
And the FilterChain:
#Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange()
.pathMatchers(constraintsConfig.getOpenUri())
.permitAll()
.and()
.authorizeExchange()
.anyExchange().authenticated()
.and()
.oauth2ResourceServer()
.jwt();
return http.build();
}

spring-boot ReactiveClientRegistrationRepository not found

Environment: Spring Boot 2.3.1, Java 11
I have tried out a few things already (also comparing with the sample-app by spring), but so far I have been unsuccessful in creating a WebClient that requires a ReactiveClientRegistrationRepository.
I get the following exception when starting up my spring-boot application:
required a bean of type 'org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository'
The way I understand spring-boot-autoconfigure it should use the ReactiveOAuth2ClientAutoConfiguration, as in the yml-file the required properties are given.
Following some code-snippets, I can provide more if something is missing to get the whole context
Main-Class
#Slf4j
#SpringBootApplication
#EnableConfigurationProperties(MyAppConfigurationProperties.class)
public class MyApp{
public static void main(final String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
Configuration:
#Configuration
//#Import(ReactiveOAuth2ClientAutoConfiguration.class) // in the test it works with this, but should not be required: spring-boot-autoconfigure
public class MyRestClientConfig {
#Bean
WebClient myWebClient(WebClient.Builder builder, ReactiveClientRegistrationRepository clientRegistrations) {
//content not relevant to this problem
}
}
Configuration for security
#EnableGlobalMethodSecurity(securedEnabled = true)
#EnableWebSecurity
#EnableWebFluxSecurity
public class SecurityConfig {
}
application.yml
spring:
security:
oauth2:
client:
registration:
providerid:
authorization-grant-type: "client_credentials"
client-id: "myClientId"
client-secret: "mySecret"
user-info-authentication-method: header
provider:
providerid:
token-uri: "working token-uri"
I tried with different dependencies, so some may not be required. Which dependencies are actually required?
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.springframework.security.oauth.boot</groupId>-->
<!-- <artifactId>spring-security-oauth2-autoconfigure</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.security.oauth</groupId>-->
<!-- <artifactId>spring-security-oauth2</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-oauth2-client</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-oauth2-core</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-oauth2-jose</artifactId>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.boot</groupId>-->
<!-- <artifactId>spring-boot-starter-webflux</artifactId>-->
<!-- </dependency>-->
In an integration-test the Spring-Boot-Application starts up
#EnableConfigurationProperties
#Import(ReactiveOAuth2ClientAutoConfiguration.class) // in the test it works with this, but should not be required: spring-boot-autoconfigure, can be omitted if added in MyRestClientConfig
#ComponentScan(basePackages = "com.mycompany")
public class ManualClientTester {
}
EDIT 1:
Debug of Positive Matches for Autoconfiguration
In test where it works:
============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
ReactiveOAuth2ClientAutoConfiguration matched:
- #ConditionalOnClass found required classes 'reactor.core.publisher.Flux', 'org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity', 'org.springframework.security.oauth2.client.registration.ClientRegistration' (OnClassCondition)
- NoneNestedConditions 0 matched 1 did not; NestedCondition on ReactiveOAuth2ClientAutoConfiguration.NonServletApplicationCondition.ServletApplicationCondition not a servlet web application (ReactiveOAuth2ClientAutoConfiguration.NonServletApplicationCondition)
ReactiveOAuth2ClientConfigurations.ReactiveClientRegistrationRepositoryConfiguration matched:
- OAuth2 Clients Configured Condition found registered clients myClientId (ClientsConfiguredCondition)
- #ConditionalOnMissingBean (types: org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; SearchStrategy: all) did not find any beans (OnBeanCondition)
ReactiveOAuth2ClientConfigurations.ReactiveOAuth2ClientConfiguration matched:
- #ConditionalOnBean (types: org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; SearchStrategy: all) found bean 'clientRegistrationRepository' (OnBeanCondition)
ReactiveOAuth2ClientConfigurations.ReactiveOAuth2ClientConfiguration#authorizedClientRepository matched:
- #ConditionalOnMissingBean (types: org.springframework.security.oauth2.client.web.server.ServerOAuth2AuthorizedClientRepository; SearchStrategy: all) did not find any beans (OnBeanCondition)
ReactiveOAuth2ClientConfigurations.ReactiveOAuth2ClientConfiguration#authorizedClientService matched:
- #ConditionalOnMissingBean (types: org.springframework.security.oauth2.client.ReactiveOAuth2AuthorizedClientService; SearchStrategy: all) did not find any beans (OnBeanCondition)
When starting my spring boot application:
============================
CONDITIONS EVALUATION REPORT
============================
Negative matches:
-----------------
ReactiveOAuth2ClientAutoConfiguration:
Did not match:
- NoneNestedConditions 1 matched 0 did not; NestedCondition on ReactiveOAuth2ClientAutoConfiguration.NonServletApplicationCondition.ServletApplicationCondition found 'session' scope (ReactiveOAuth2ClientAutoConfiguration.NonServletApplicationCondition)
Matched:
- #ConditionalOnClass found required classes 'reactor.core.publisher.Flux', 'org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity', 'org.springframework.security.oauth2.client.registration.ClientRegistration' (OnClassCondition)
EDIT 2:
After changing as suggested, I have now the following:
#EnableReactiveMethodSecurity
#EnableWebFluxSecurity
public class SecurityConfig {
}
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
Furthermore all used versions of spring-projects:
<spring-amqp.version>2.2.7.RELEASE</spring-amqp.version>
<spring-batch.version>4.2.4.RELEASE</spring-batch.version>
<spring-boot.version>2.3.1.RELEASE</spring-boot.version>
<spring-data-releasetrain.version>Neumann-SR1</spring-data-releasetrain.version>
<spring-framework.version>5.2.7.RELEASE</spring-framework.version>
<spring-hateoas.version>1.1.0.RELEASE</spring-hateoas.version>
<spring-integration.version>5.3.1.RELEASE</spring-integration.version>
<spring-kafka.version>2.5.2.RELEASE</spring-kafka.version>
<spring-ldap.version>2.3.3.RELEASE</spring-ldap.version>
<spring-restdocs.version>2.0.4.RELEASE</spring-restdocs.version>
<spring-retry.version>1.2.5.RELEASE</spring-retry.version>
<spring-security.version>5.3.3.RELEASE</spring-security.version>
<spring-session-bom.version>Dragonfruit-RELEASE</spring-session-bom.version>
<spring-ws.version>3.0.9.RELEASE</spring-ws.version>
<spring.boot.version>2.3.1.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR5</spring.cloud.version>
The problem still exists.
I ran into the same problem and noticed that the application created a ClientRegistrationRepository instead of a ReactiveClientRegistrationRepository. Somewhere in Spring boot the #EnableWebSecurity was added (we need the #EnableWebFluxSecurity in this case).
To fix the problem I've added the following property:
spring.main.web-application-type: reactive
If you're also using #SpringBootTest to test your application you also need to add the property there.
#SpringBootTest(properties = ["spring.main.web-application-type=reactive]")
or by setting the web environment to NONE
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
The reason why this happens is also explained in this answer: Error when using #EnableWebFluxSecurity in springboot
I'm still not happy about my solution, but I ended up doing the following:
#Bean
public ReactiveClientRegistrationRepository reactiveClientRegistrationRepository(OAuth2ClientProperties oAuth2ClientProperties) {
List<ClientRegistration> clientRegistrations = new ArrayList<>();
// because autoconfigure does not work for an unknown reason, here the ClientRegistrations are manually configured based on the application.yml
oAuth2ClientProperties.getRegistration()
.forEach((k, v) -> {
String tokenUri = oAuth2ClientProperties.getProvider().get(k).getTokenUri();
ClientRegistration clientRegistration = ClientRegistration
.withRegistrationId(k)
.tokenUri(tokenUri)
.clientId(v.getClientId())
.clientSecret(v.getClientSecret())
.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
.build();
clientRegistrations.add(clientRegistration);
});
return new InMemoryReactiveClientRegistrationRepository(clientRegistrations);
}
I use the spring-properties for OAuth and then create the ReactiveClientRegistrationRepository based on those properties.
Works this way:
#Bean
public WebClient webClient(ClientRegistrationRepository clientRegistrationRepository) {
InMemoryReactiveClientRegistrationRepository registrationRepository = new InMemoryReactiveClientRegistrationRepository(clientRegistrationRepository.findByRegistrationId("REG_ID"));
InMemoryReactiveOAuth2AuthorizedClientService clientService = new InMemoryReactiveOAuth2AuthorizedClientService(registrationRepository);
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager clientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(registrationRepository, clientService);
return WebClient.builder()
.baseUrl(BASEURL)
.filter(new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientManager))
.build();
}
Properties:
spring.security.oauth2.client.registration.REG_ID.client-id=CLIENT_ID
spring.security.oauth2.client.registration.REG_ID.client-name=CLIENT_NAME
spring.security.oauth2.client.registration.REG_ID.client-secret=SECRET
spring.security.oauth2.client.registration.REG_ID.authorization-grant-type=client_credentials
spring.security.oauth2.client.registration.REG_ID.scope=SCOPE
spring.security.oauth2.client.provider.REG_ID.issuer-uri=PATH_TO_.well-known/openid-configuration_SITE
EDIT:
Add the following before the return statement:
ServerOAuth2AuthorizedClientExchangeFilterFunction clientExchangeFilterFunction = new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientManager);
clientExchangeFilterFunction.setDefaultClientRegistrationId("REG_ID");
And replace filter in the return statement with:
.filter(clientExchangeFilterFunction)
I solved this by writing this code
#Bean("oauthWebClient")
WebClient webClient(ClientRegistrationRepository clientRegistrations) {
InMemoryReactiveClientRegistrationRepository registrationRepository = new InMemoryReactiveClientRegistrationRepository(
clientRegistrations.findByRegistrationId("reg-id"));
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServerOAuth2AuthorizedClientExchangeFilterFunction(
registrationRepository,
new UnAuthenticatedServerOAuth2AuthorizedClientRepository());
oauth.setDefaultClientRegistrationId("reg-id");
return WebClient.builder()
.filter(oauth)
.build();
}
Update:
Looks like the "user-info-authentication-method" (userInfoAuthenticationMethod) is part of the Provider and not the Registration. And please remove the double quotes too.
spring:
security:
oauth2:
client:
registration:
providerid:
authorization-grant-type: client_credentials
client-id: myClientId
client-secret: mySecret
provider:
providerid:
token-uri: <working token-uri>
user-info-authentication-method: header
Also a suggestion - to avoid possible conflicting/ incompatible dependencies, please use dependency management like this and try to have all spring boot starters. eg the spring security library comes as part of both spring-boot-starter-oauth2-client and spring-boot-starter-oauth2-resource-server:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Just these 2 dependencies should do the work: (these are picked from Gradle file, please change them to POM equivalent)
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
For tests, probably you may need this:
testImplementation 'org.springframework.security:spring-security-test:5.3.3.RELEASE'
You should not mix the two together:
#EnableWebSecurity
#EnableWebFluxSecurity
If your application is reactive, then just use #EnableWebFluxSecurity.
And coming to #EnableGlobalMethodSecurity(securedEnabled = true) , this is described here, and is recommended to remove that and use this instead:
#EnableReactiveMethodSecurity
it's difficult to provide relevant answer without a stacktrace.
Seem that Spring boot cant create ReactiveClientRegistrationRepository from your properties file.
Try to add a provider property on your client.
oauth2:
client:
registration:
registrationId:
provider: providerId
client-id: clientId
client-secret: secret
authorization-grant-type: client_credentials
ReactiveClientRepositoryRegistration Bean needs to be defined explicitly. You can refer to the spring documentation
https://docs.spring.io/spring-security/reference/reactive/oauth2/login/core.html#webflux-oauth2-login-register-reactiveclientregistrationrepository-bean

Spring OIDC with OpenAM

I'd like to handle an authorization code grant flow with spring.
It "works" for the redirections, on arriving on my app, correctly redirected to openam, authN and grant authZ, submit, redirected to redirectUri.
But when I come back in the redirectUri, it seems Spring doesn't go inside my method (doesn't stop on my breakpoint), and the security context is probably empty, so not authenticated, so redirected back to auth.
Here's my config, am I missing something ?
REST api in Springboot, with angular frontend
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath />
</parent>
...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
#Configuration
#EnableWebSecurity
public class SecurityConfigOAuth2 extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
.and()
.authorizeRequests()
.antMatchers("/REST/v1/authorization-code/**").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.oauth2Login()
.and()
.oauth2Client()
;
}
}
server:
port: 8080
servlet:
context-path: /OAuth2Client
spring:
application:
name: OAuth2Client
security:
basic:
enabled: false
oauth2:
client:
registration:
openam:
clientId: MyClientId
scope:
- openid
- profile
- email
authorizationGrantType: authorization_code
---
spring:
profiles: dev
security:
oauth2:
client:
provider:
openam:
tokenUri: {openam-dev}/access_token
authorizationUri: {openam-dev}/authorize
userInfoUri: {openam-dev}/userinfo
tokenInfoUri: {openam-dev}/tokeninfo
jwkSetUri: {openam-dev}/jwk_uri
registration:
openam:
clientSecret: MyClientSecret-dev
redirectUri: "{baseUrl}/REST/v1/authorization-code/callback"
logging:
level:
org.springframework.security: DEBUG
org.springframework.web.client.RestTemplate: DEBUG

Spring Cloud Config unable to get correct values

I am unable to get the value I am expecting, An exception is thrown at this line #Value("${message:this-is-class-value}").
SERVER SIDE
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
src/main/resources/application.properties
server.port=8888
spring.application.name=config-service
spring.cloud.config.server.git.uri=file:///C:/config
management.endpoints.web.exposure.include=*
spring.security.user.name=root
spring.security.user.password=abc123
Application class
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;
#SpringBootApplication
#EnableConfigServer
public class ConfigServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServiceApplication.class, args);
}
}
Local git folder
Configurations files with the same property but the different value to detect
c:/config/application.properties
c:/config/api-gateway.properties
c:/config/api-gateway-PROD.properties
output while server startup
Completed initialization in 5 ms
WARN : Could not merge remote for master remote: null
INFO : o.s.c.c.s.e.NativeEnvironmentRepository : Adding property source: file:/C:/config/application.properties
if I access this url
http://localhost:8888/api-gateway/PROD
console output is as follows
WARN : Could not merge remote for master remote: null
INFO : o.s.c.c.s.e.NativeEnvironmentRepository : Adding property source: file:/C:/config/api-gateway-PROD.properties
INFO : o.s.c.c.s.e.NativeEnvironmentRepository : Adding property source: file:/C:/config/api-gateway.properties
INFO : o.s.c.c.s.e.NativeEnvironmentRepository : Adding property source: file:/C:/config/application.properties
CLIENT SIDE (Separate Project)
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
Application class
#SpringBootApplication
#EnableZuulProxy
#EnableDiscoveryClient
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
Controller
#RestController
public class SettingsController {
#Value("${message:this-is-class-value}")
String name = "World";
#RequestMapping("/")
public String home() {
return "Hello " + name;
}
}
resources/application.yml
server:
port: 8282
spring:
application:
name: api-gateway
eureka:
instance:
preferIpAddress: true
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}
resources/bootstrap.yml
spring:
profiles:
active: PROD
cloud:
config:
name: api-gateway
uri: http://localhost:8888/
username: root
password: abc123
management:
endpoints:
web:
exposure:
include: refresh
Console output
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'settingsController': Injection of autowired dependencies failed; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.util.LinkedHashMap<?, ?>] to type [java.lang.String]
Caused by: org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [java.util.LinkedHashMap<?, ?>] to type [java.lang.String]
Please do let me know if anything else is required.
add message=Hello from property file1 in src/main/resources/application.properties. Or if you want to read this property from c:/config/application.properties or c:/config/config-service.properties you need to configure external configuration file by using #ConfigurationProperties annotation.
Looks, you are trying to use the #Value annotation on controller class which resides in your Cloud config server application. While the concept is like cloud config server application will be the provider to other applications and it can provide the properties from git or local file system (configuration) to requester client application. Other applications will connect to your config server application by providing it's URL, application name and profile for which it want to get the properties. You can check below URL for cloud config server and client application.
https://www.thetechnojournals.com/2019/10/spring-cloud-config.html
Your cloud config server has security configuration as below.
spring.security.user.name=root
spring.security.user.password=abc123
You client configuration doesn't provide any security configuration which is causing 401 error in your logs.
To solve this issue please do below changes to your client configuration.
Client configuration:
spring:
profiles:
active: PROD
cloud:
config:
name: api-gateway
uri: http://localhost:8888/
username: root
password: abc123

Categories

Resources