Skip to content

Client Credentials Client Server#

  • In the Client Credentials Resource Server, we learned how to config Spring Boot Resource Server with Keycloak authorization server for Client Credentials Grant Type using Token Signature Resource Server and we used Postman as the a client to get access token and use it to call to the Spring Boot resource server manually.
  • In this section, we will continue to configure a Spring Boot client server which will connect to Keycloak authorization server to get token and call to Resource server automatically when the user call to client server to get the data.
  • If you haven't know about Token Signature Resource Server, you can view this post: OAUTH2 Resource Server Token Validation.

Prepare#

  • So firstly, we need to overview the simple diagram that we are going to do as in the image below.

 #zoom

  • So as you can see, when the user use Postman to call to the Spring Boot Client application then it will automatically call to the Keycloak authorization server with the client credentials to get the access token and then call to the Spring Boot Resource Server application to get the data and return to the Postman.
  • For setting up Keycloak authorization server please view Keycloak Setup with Client Credentials Grant Type.
  • For setting up Spring Boot Resource server with Keycloak please view Client Credentials Resource Server.

Understand OAuth2 Client Dependencies#

  • Before going the implementation details, we should take a look through the core interfaces/classes of the spring-security-oauth2-client dependencies which we will use in the Spring Boot service to create Spring Boot client server with Keycloak. Then later, we will know what are we configuring and why do we need to configure them.
  • The dependency for Spring Boot service will look like as below:
pom.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
.....

<!-- Spring Boot oauth2 client -->
<dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-client</artifactId>
        <version>5.6.0</version>
</dependency>

.....
  • So in the spring-security-oauth2-client dependencies there are 6 main interfaces/classes.

ClientRegistration#

  • ClientRegistration is a representation of a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.
  • A client registration holds information, such as client id, client secret, authorization grant type, redirect URI, scope(s), authorization URI, token URI, and other details.
  • ClientRegistration and its properties are defined as follows:
ClientRegistration.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public final class ClientRegistration {
    private String registrationId;  
    private String clientId;    
    private String clientSecret;    
    private ClientAuthenticationMethod clientAuthenticationMethod;  
    private AuthorizationGrantType authorizationGrantType;  
    private String redirectUri; 
    private Set<String> scopes; 
    private ProviderDetails providerDetails;
    private String clientName;  

    public class ProviderDetails {
        private String authorizationUri;    
        private String tokenUri;    
        private UserInfoEndpoint userInfoEndpoint;
        private String jwkSetUri;   
        private String issuerUri;   
        private Map<String, Object> configurationMetadata;  

        public class UserInfoEndpoint {
            private String uri; 
            private AuthenticationMethod authenticationMethod;  
            private String userNameAttributeName;   

        }
    }
}
Configuration Properties Descriptions
registrationId The ID that uniquely identifies the ClientRegistration. Ex: keycloak, okta .etc..
clientId The client identifier.
clientSecret The client secret.
clientAuthenticationMethod The method used to authenticate the Client with the Provider. The supported values are basic, post and none (public clients).
authorizationGrantType The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials and password.
redirectUri The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.
scopes The scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.
clientName A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.
authorizationUri The Authorization Endpoint URI for the Authorization Server.
tokenUri The Token Endpoint URI for the Authorization Server.
jwkSetUri The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and optionally the UserInfo Response.
issuerUri Returns the issuer identifier uri for the OpenID Connect 1.0 provider or the OAuth 2.0 Authorization Server.
configurationMetadata The OpenID Provider Configuration Information. This information will only be available if the Spring Boot 2.x property spring.security.oauth2.client.provider.{{providerId}}.issuerUri is configured.
(userInfoEndpoint)uri The UserInfo Endpoint URI used to access the claims/attributes of the authenticated end-user.
(userInfoEndpoint)authenticationMethod The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form and query.
userNameAttributeName The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.
  • Client registrations are typically loaded automatically from an application.yml/application.properties file. Spring auto-configuration looks for properties with the schema spring.security.oauth2.client.registration.[registrationId] and creates a ClientRegistration instance within a ClientRegistrationRepository. See the example configuration in application.yml below.
application.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
spring:
  security:
    oauth2:
      client:
        registration:
          keycloak: # <--- It's your custom client. I am using keycloak
            client-id: myclient
            client-secret: 80YlNd6qwz84hnisibF8QHVAtjUkVj0p
            authorization-grant-type: client_credentials
            scope: # your scopes
              - openid
              - address
              - email
              - profile
  • Below is the table that contains all configuration that we can apply in to application.yml/application.properties for ClientRegistration configurations.
Spring Boot 2.x ClientRegistration
spring.security.oauth2.client.registration.[registrationId] registrationId
spring.security.oauth2.client.registration.[registrationId].client-id clientId
spring.security.oauth2.client.registration.[registrationId].client-secret clientSecret
spring.security.oauth2.client.registration.[registrationId].client-authentication-method clientAuthenticationMethod
spring.security.oauth2.client.registration.[registrationId].authorization-grant-type authorizationGrantType
spring.security.oauth2.client.registration.[registrationId].redirect-uri redirectUri
spring.security.oauth2.client.registration.[registrationId].scope scopes
spring.security.oauth2.client.registration.[registrationId].client-name clientName
spring.security.oauth2.client.provider.[providerId].authorization-uri providerDetails.authorizationUri
spring.security.oauth2.client.provider.[providerId].token-uri providerDetails.tokenUri
spring.security.oauth2.client.provider.[providerId].jwk-set-uri providerDetails.jwkSetUri
spring.security.oauth2.client.provider.[providerId].issuer-uri providerDetails.issuerUri
spring.security.oauth2.client.provider.[providerId].user-info-uri providerDetails.userInfoEndpoint.uri
spring.security.oauth2.client.provider.[providerId].user-info-authentication-method providerDetails.userInfoEndpoint.authenticationMethod
spring.security.oauth2.client.provider.[providerId].user-name-attribute providerDetails.userInfoEndpoint.userNameAttributeName

ClientRegistrationRepository#

  • The ClientRegistrationRepository serves as a repository for OAuth 2.0 / OpenID Connect 1.0 ClientRegistration(s). ClientRegistrationRepository is a container class that holds ClientRegistrations.

    Client registration information is ultimately stored and owned by the associated Authorization Server. This repository provides the ability to retrieve a sub-set of the primary client registration information, which is stored with the Authorization Server.

OAuth2AuthorizedClient#

  • OAuth2AuthorizedClient: Represents an authorized client. This is a composed class that contains a client registration but adds authentication information. Take a look at the code excerpt below. You’ll see that the OAuth2AuthorizedClient adds three properties composed on top of the client registration: a principal name, an access token, and a refresh token.
OAuth2AuthorizedClient.java
1
2
3
4
5
6
7
8
public class OAuth2AuthorizedClient implements Serializable {
    ...
    private final ClientRegistration clientRegistration;
    private final String principalName;
    private final OAuth2AccessToken accessToken;
    private final OAuth2RefreshToken refreshToken;
    ...
}

OAuth2AuthorizedClientRepository#

  • OAuth2AuthorizedClientRepository: is a container class that holds and persists authorized clients between requests. The default implementation, InMemoryOAuth2AuthorizedClientService, simply stores the clients in memory.

OAuth2AuthorizedClientManager#

  • OAuth2AuthorizedClientManager: is the manager class that contains the logic to handle the authorization flow. Most importantly, it authorizes and re-authorizes OAuth 2.0 clients using an OAuth2AuthorizedClientProvider. It also delegates persistence of the authorized clients and calls success or failure handlers when client authorization succeeds or fails.

OAuth2AuthorizedClientProvider#

  • OAuth2AuthorizedClientProvider: represents an OAuth 2.0 provider and handles the actual request logic for different grant types and OAuth 2.0 providers. They can be auto-configured based on property values (spring.security.oauth2.client.provider.[provider name]). In the case of this tutorial, we will be using Keycloak as our provider, so we’ll see properties with the prefix spring.security.oauth2.client.provider.keycloak. that are auto-configuring an associated OAuth2AuthorizedClientProvider.
application.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
spring:  
  security:  
    oauth2:  
      client:  
        registration:  
          keycloak: # <--- It's your custom client. I am using keycloak  
            client-id: myclient  
            client-secret: 80YlNd6qwz84hnisibF8QHVAtjUkVj0p  
            authorization-grant-type: client_credentials  
            scope: # your scopes  
              - openid  
              - address  
              - email  
              - profile  
        provider:  
          keycloak: # <--- Here Registered my custom provider  
            authorization-uri: http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/auth  
            token-uri: http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token

Client Server Setup#

Dependencies#

  • Now, let's create a Spring Boot application and add some dependencies as below.
pom.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
 <dependencyManagement>
            <dependencies>
                    <dependency>
                            <groupId>org.springframework.cloud</groupId>
                            <artifactId>spring-cloud-dependencies</artifactId>
                            <version>2021.0.0</version>
                            <type>pom</type>
                            <scope>import</scope>
                    </dependency>
            </dependencies>
    </dependencyManagement>

    <dependencies>
            <!--spring boot starter-->
            <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-web</artifactId>
                    <version>2.6.3</version>
            </dependency>

            <!--lombok-->
            <dependency>
                    <groupId>org.projectlombok</groupId>
                    <artifactId>lombok</artifactId>
                    <version>1.18.22</version>
                    <scope>provided</scope>
            </dependency>

            <!--Spring cloud openfeign-->
            <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-openfeign</artifactId>
                    <version>3.1.0</version>
            </dependency>

            <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
                    <version>3.1.0</version>
            </dependency>

            <!-- Spring security -->
            <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-security</artifactId>
                    <version>2.6.1</version>
            </dependency>

            <!-- Spring Boot oauth2 client -->
            <dependency>
                    <groupId>org.springframework.security</groupId>
                    <artifactId>spring-security-oauth2-client</artifactId>
                    <version>5.6.0</version>
            </dependency>

    </dependencies>
  • To make Spring Boot application become client server we need to apply spring-boot-starter-security and spring-security-oauth2-client dependencies and also spring-cloud-starter-openfeign to make the call with the access token from the client to the resource server, this is the service to service communication.

OAuth2 Client Configuration#

  • Firstly, let's create the class OAuth2Config.java to create the bean OAuth2AuthorizedClientManager bean as below:
OAuth2Config.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.springboot.cloud.openfeign.client.credentials.interceptor.config.oauth2.v1;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;

@Configuration
public class OAuth2Config {

    @Bean
    public OAuth2AuthorizedClientManager authorizedClientManager(
            final ClientRegistrationRepository clientRegistrationRepository,
            final OAuth2AuthorizedClientService authorizedClientService) {
        return new AuthorizedClientServiceOAuth2AuthorizedClientManager(
                clientRegistrationRepository, authorizedClientService);
    }

}
  • We will create the OAuth2AuthorizedClientManager bean by new AuthorizedClientServiceOAuth2AuthorizedClientManager and we need to inject 2 beans which are ClientRegistrationRepository and OAuth2AuthorizedClientService.

  • Next, let's create a service class OAuth2ClientService.java with a method for getting the access token from the Keycloak authorization server as below.

OAuth2ClientService.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.springboot.cloud.openfeign.client.credentials.interceptor.config.oauth2.v1;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.stereotype.Service;

import static java.util.Objects.isNull;


@Slf4j
@Service
public class OAuth2ClientService {

    private final OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager;

    private static final Authentication ANONYMOUS_USER_AUTHENTICATION =
            new AnonymousAuthenticationToken("key", "anonymous", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));


    @Autowired
    public OAuth2ClientService(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) {
        this.oAuth2AuthorizedClientManager = oAuth2AuthorizedClientManager;
    }

    public String getAccessToken(String clientRegistrationId) {
        OAuth2AuthorizeRequest oAuth2AuthorizeRequest = OAuth2AuthorizeRequest
                .withClientRegistrationId(clientRegistrationId)
                .principal(ANONYMOUS_USER_AUTHENTICATION)
                .build();
        OAuth2AuthorizedClient client = oAuth2AuthorizedClientManager.authorize(oAuth2AuthorizeRequest);
        if (isNull(client)) {
            throw new IllegalStateException("client credentials flow on " + clientRegistrationId + " failed, client is null");
        }
        return "Bearer " + client.getAccessToken().getTokenValue();
    }

}
  • So, in this OAuth2ClientService, we will use the OAuth2AuthorizedClientManager to authorize with the Keycloak authorization server with an OAuth2AuthorizeRequest and receive the OAuth2AuthorizedClient, then from this OAuth2AuthorizedClient we can extract the access token.

  • Finally, we also need to put some configurations in the application.yml for the spring security oauth2 client.

application.yml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
server:
  port: 8086

spring:
  security:
    oauth2:
      url: http://localhost:8085
      client:
        registration:
          keycloak: # <--- It's your custom client. I am using keycloak
            client-id: myclient
            client-secret: 80YlNd6qwz84hnisibF8QHVAtjUkVj0p
            authorization-grant-type: client_credentials
            scope: openid, address, email, profile # your scopes
        provider:
          keycloak: # <--- Here Registered my custom provider
            authorization-uri: http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/auth
            token-uri: http://localhost:8080/auth/realms/myrealm/protocol/openid-connect/token


logging:
  level:
    com.springboot.cloud.openfeign.client.credentials.interceptor.api: DEBUG

feign:
  client:
    config:
      default:
        loggerLevel: full

Feign Configuration#

  • Next, to use the access token and call to the Spring Boot Resource Server for every request, we need to create a feign interceptor config as below.
OAuth2FeignConfig.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.springboot.cloud.openfeign.client.credentials.interceptor.config.oauth2.v1;


import feign.RequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpHeaders;

public class OAuth2FeignConfig {

    public static final String CLIENT_REGISTRATION_ID = "keycloak";

    private final OAuth2ClientService oAuth2ClientService;

    public OAuth2FeignConfig(OAuth2ClientService oAuth2ClientService) {
        this.oAuth2ClientService = oAuth2ClientService;
    }

    @Bean
    public RequestInterceptor requestInterceptor() {
        return requestTemplate -> {
            requestTemplate.header(HttpHeaders.AUTHORIZATION, this.oAuth2ClientService.getAccessToken(CLIENT_REGISTRATION_ID));
        };
    }

}
  • So in this config we will use the OAuth2ClientService to get the access token and put it into the Authorization header of requests to Spring Boot Resource Server.

  • Next, we also need to create an adapter interface name SpringOAuth2ResourceClient to configure FeignClient with target api as below:

SpringOAuth2ResourceClient.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package com.springboot.cloud.openfeign.client.credentials.interceptor.api;

import com.springboot.cloud.openfeign.client.credentials.interceptor.config.oauth2.v1.OAuth2FeignConfig;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@FeignClient(name = "springOAuth2ResourceClient", url = "${spring.security.oauth2.url}", configuration = {OAuth2FeignConfig.class})
public interface SpringOAuth2ResourceClient {

    @RequestMapping(method = RequestMethod.GET, path = "/v1/card")
    String getCardDetail();

}
  • We will put the FeignClient configuration class that we created in the step above into this FeignClient by using param configuration. So now, every request of the api /v1/card to the Spring Boot resource server will have the access token which is fetched from the Keycloak authorization server.

  • Finally, in the main class, we just need to put annotation @EnableFeignClients as below.

OpenFeignClientCredentialsInterceptorApplication.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package com.springboot.cloud.openfeign.client.credentials.interceptor;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@EnableFeignClients
@SpringBootApplication
public class OpenFeignClientCredentialsInterceptorApplication {
    public static void main(String[] args) {
        SpringApplication.run(OpenFeignClientCredentialsInterceptorApplication.class, args);
    }
}

Controller#

  • Let's create a simple controller in the Spring Boot client application which will be called by the Postman as below.
OpenFeignInterceptorController.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package com.springboot.cloud.openfeign.client.credentials.interceptor.controller;

import com.springboot.cloud.openfeign.client.credentials.interceptor.api.SpringOAuth2ResourceClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OpenFeignInterceptorController {

    @Autowired
    private SpringOAuth2ResourceClient springOAuth2ResourceClient;

    @RequestMapping(method = RequestMethod.GET, path = "/v1/oauth2/auth/interceptor/card")
    public ResponseEntity<String> getCardMessage() {
        return ResponseEntity.ok(this.springOAuth2ResourceClient.getCardDetail());
    }

}
  • So when this api is called by postman then it will use the SpringOAuth2ResourceClient to call to the Spring Boot resource server to get the data.

Basic Security Configuration#

  • We will increase the security for our Spring Boot client service a little bit. In detail, to call the api in the controller of the step above, the user from Postman must put a basic authentication with username and password.
  • So let's create a SecurityConfig.java class and put some basic authentication configuration as below.
SecurityConfig.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.springboot.cloud.openfeign.client.credentials.interceptor.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.Collections;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and().cors().configurationSource(this.corsConfigurationSource())
                .and().csrf().disable()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and().formLogin()
                .and().httpBasic();
        return http.build();
    }

    @Bean
    protected CorsConfigurationSource corsConfigurationSource() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(Collections.singletonList("*"));
        corsConfiguration.setAllowedOriginPatterns(Collections.singletonList("*"));
        corsConfiguration.setAllowedMethods(Collections.singletonList("*"));
        corsConfiguration.setAllowCredentials(true);
        corsConfiguration.setExposedHeaders(Collections.singletonList("AUTHORIZATION"));
        corsConfiguration.setMaxAge(3600L);
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }

    @Bean
    public UserDetailsService inMemoryUserDetailsService() {
        UserDetails demo = User.withUsername("user").password("12345").roles("POSTMAN").build();
        return new InMemoryUserDetailsManager(demo);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

}

Note: The properties under spring.security.user won't work because Spring Boot will back off creating the UserDetailService bean. So, we will have to define the UserDetailsService bean by our self.

Testing#

  • Now, let's use Postman to call to api /v1/oauth2/auth/interceptor/card of our Spring Boot Client server, then you can receive the data of Sping Boot Resource server successfully as below.

 #zoom

  • If you look into the our Spring Boot Client service then you can see the access token is added into the Authorization header of the request to the Spring Boot Resource server as below.

 #zoom

  • It means, our implementation is successful.
  • Now, if you make more calls from Postman, you can see the access token has not changed, so our Spring Boot Client server had reused the access token because it is still not expired.

 #zoom

  • Then if you wait a little bit and make a call from Postman again, then you can see in the log we will have a new access token. So it means the old access token has been expired so our Spring Boot Client server will call to Keycloak authorization server to get the access token again.

 #zoom

See Also#

References#