After this steps, you can use KeycloakRestTemplate
to access the secured microservice (Relay the Token). However,
this only works if the request is initialized by a user. See OAuth2 WebClient
Configuration for Sending requests from
Microservice to Microservice.
- Add following dependencies to your project
<depencencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-security-adapter</artifactId>
<version>16.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</depencencies>
- Use environment variables or add properties to the application.properties file to configure the Keycloak server
keycloak:
realm: ${AUTH_REALM:<realm-name>}
resource: ${AUTH_RESOURCE:<client-id>}
auth-server-url: ${AUTH_SERVER:<server-url>}
credentials.secret: ${AUTH_SECRET:<keyclaok secret>}
bearer-only: true
- Create Keycloak Configuration Class
import org.keycloak.adapters.springsecurity.KeycloakSecurityComponents;
import org.keycloak.adapters.springsecurity.authentication.KeycloakAuthenticationProvider;
import org.keycloak.adapters.springsecurity.client.KeycloakClientRequestFactory;
import org.keycloak.adapters.springsecurity.client.KeycloakRestTemplate;
import org.keycloak.adapters.springsecurity.config.KeycloakWebSecurityConfigurerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.authority.mapping.SimpleAuthorityMapper;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.web.authentication.session.RegisterSessionAuthenticationStrategy;
import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy;
import org.springframework.web.client.DefaultResponseErrorHandler;
/**
* @author Pavith Madusara
* Created at 08-Feb-2022
* Wednesday at 2:24 PM
*/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(jsr250Enabled = true)
@ComponentScan(basePackageClasses = KeycloakSecurityComponents.class)
public class KeyCloakSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
@Autowired
public KeycloakClientRequestFactory keycloakClientRequestFactory;
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public KeycloakRestTemplate keycloakRestTemplate() {
KeycloakRestTemplate restTemplate = new KeycloakRestTemplate(keycloakClientRequestFactory);
restTemplate.setErrorHandler(new DefaultResponseErrorHandler());
return restTemplate;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http.authorizeRequests()
.anyRequest()
.permitAll();
http.csrf().disable();
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(new SimpleAuthorityMapper());
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
}
- Add Config Resolver to Application Class
public class MyApplication{
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
@Bean
public KeycloakConfigResolver KeycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
}
- RestTemplate and KeycloakRestTemplate Usage
@Service
public class MediaService {
private final KeycloakRestTemplate restTemplate;
@Value("${gateway.rootUrl}")
private String gatewayUrl;
public MediaService(KeycloakRestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
public BucketEntityDTO getBucketEntity(Long id) {
RestTemplate restTemplate = new RestTemplate();
return restTemplate.getForObject(gatewayUrl + "/media/api/v1/bucket-entities/{id}", BucketEntityDTO.class, id);
}
public GalleryDTO createGallery(GalleryDTO galleryDTO) {
return restTemplate.postForObject(gatewayUrl + "/media/api/v1/galleries", galleryDTO, GalleryDTO.class);
}
}
- Add following dependencies to the project
<dependencies>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
- Add following configuration to the application.properties file
spring:
security:
oauth2:
client:
registration:
keycloak:
client-id: ${keycloak.resource}
client-secret: ${keycloak.credentials.secret}
authorization-grant-type: client_credentials
provider:
keycloak:
token-uri: ${keycloak.auth-server-url}/realms/${keycloak.realm}/protocol/openid-connect/token
- Create OAuth2 WebClient Configuration Class
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.*;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
import org.springframework.web.reactive.function.client.WebClient;
@Configuration
public class OAuth2WebClientConfig {
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService clientService)
{
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceOAuth2AuthorizedClientManager(
clientRegistrationRepository, clientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
return authorizedClientManager;
}
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("keycloak");
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
}
- WebClient Usage
@Service
public class RolesService {
@Autowired
private WebClient webClient;
@Value("${gateway.rootUrl}")
private String gatewayURL;
public void createRole(RoleDTO roleDTO) {
webClient.post()
.uri(gatewayURL + "/auth/api/v1/roles")
.bodyValue(roleDTO)
.retrieve()
.bodyToMono(RoleDTO.class)
.block();
}
}