1

I have a microservice landscape configured with Spring Cloud discovery so I'm able to access other service instances just using their id's:

public class MyClass {

    @Autowired
    @LoadBalanced
    private RestTemplate restTemplate;

    public String doOtherStuff() {
        String results = restTemplate.getForObject("http://stores/stores", String.class);
        return results;
    }
}

Now I want to access a service which needs OAuth2 authorization. I use a Keycloak server in order to provide it and Keycloak already provides an adapter with an specific KeycloakRestTemplate. Anyway, how to enhance it with load balancing?

Aritz
  • 30,971
  • 16
  • 136
  • 217

2 Answers2

2

We need to create a specific KeycloakRestTemplate which will use a LoadBalancerInterceptor:

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {

    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    @LoadBalanced
    public KeycloakRestTemplate keycloakRestTemplate(
            KeycloakClientRequestFactory keycloakClientRequestFactory,
            LoadBalancerInterceptor interceptor) {
        KeycloakRestTemplate result = new KeycloakRestTemplate(
            keycloakClientRequestFactory);
        // Add the interceptor for load balancing
        result.getInterceptors().add(interceptor);
        return result;
    }

    //More configurations for keycloak

}

So there's the chance of getting an Authorized / LoadBalanced template:

@Autowired
@LoadBalanced
protected KeycloakRestTemplate restTemplate;

See also:

Community
  • 1
  • 1
Aritz
  • 30,971
  • 16
  • 136
  • 217
0

your solution is not realy good, because

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@LoadBalanced
public KeycloakRestTemplate keycloakRestTemplate(
        KeycloakClientRequestFactory keycloakClientRequestFactory,
        LoadBalancerInterceptor interceptor) {
    KeycloakRestTemplate result = new KeycloakRestTemplate(
        keycloakClientRequestFactory);
    // Add the interceptor for load balancing
    result.getInterceptors().add(interceptor);
    return result;
}

not has worked, becouse you have got exception

The dependencies of some of the beans in the application context form a cycle:
...
┌─────┐
|  keycloakRestTemplate defined in class path resource [...]
↑     ↓
|  ribbonInterceptor defined in class path resource [org/springframework/cloud/client/loadbalancer/LoadBalancerAutoConfiguration$LoadBalancerInterceptorConfig.class]
↑     ↓
|  org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration     (field private java.util.List org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration.restTemplates)
└─────┘

What do you have to do?

@Bean
@LoadBalanced
public KeycloakRestTemplate keycloakRestTemplate(
        KeycloakClientRequestFactory keycloakClientRequestFactory) {
    return new KeycloakRestTemplate(keycloakClientRequestFactory);
}

without @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) then you get singleton which you cane use like this

@Autowired
protected KeycloakRestTemplate restTemplate;

or you can define restTemplate like prototype

@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@LoadBalanced
public KeycloakRestTemplate keycloakRestTemplate(
        KeycloakClientRequestFactory keycloakClientRequestFactory) {
    return new KeycloakRestTemplate(keycloakClientRequestFactory);
}

and then use like this

@Autowired
@LoadBalanced
protected KeycloakRestTemplate restTemplate;

Edit: The solution of Xtreme Biker has not worked with SpringBoot 2 and Keycloak 6 because of problem with cycle, my first proposition is not threads/sesions save, the second does not work because of been will be createing before run @LoadBalanced and the restTemplate wchich is created based on prototype is to without sets interceptor :|

qruk
  • 11
  • 1
  • This could be related with your security configuration or the library versions being used. I tested my answer and was working as of 2015, when I wrote it. Anyway, keycloak explicitly states their template needs to be prototype scoped (I guess it's not a thread-safe implementation: https://www.keycloak.org/docs/latest/securing_apps/index.html#client-to-client-support ). By the way, I wasn't the one downvoting you, but still I think you need to reconsider this answer ;-) – Aritz Oct 02 '19 at 07:46
  • You have right, keycloakRestTemplate has to be creating as prototype for threads/sesions safe. My answer is based on SpringBoot 2 and Keycloak 7 and this case correct will be the second solution. – qruk Oct 02 '19 at 09:29
  • Thanks @qruk. Removing @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) made it work. And as you said, the selected answer gave me the cyclic error. – Krishnakumar R Nov 22 '21 at 15:18