Keycloak spring security client credential grant

前端 未结 2 1738
执笔经年
执笔经年 2020-12-10 08:04

I can use KeycloakRestTemplate where one keycloak client is communicating with another keycloak client. However it only works if I have logged into the first ke

相关标签:
2条回答
  • 2020-12-10 08:14

    For my microservice architecture based application, I'm using both user and service accounts. I guess the spring security adapter only takes care of the user related stuff (the version I'm using, at least, which is 2.2.1). What I do is to have another RestTemplate, one which I handle myself in order to access resources as a client.

    As an example:

    @Service
    public class RemoteAccessService{
    
        //Manages user access
        private KeycloakRestTemplate userAccessRestTemplate;
    
        //Manages client access
        private RestTemplate clientAccessRestTemplate;
    
        public RemoteAccessService(KeycloakRestTemplate userAccessRestTemplate, 
            @Qualifier("clientAccessRestTemplate") RestTemplate clientAccessRestTemplate;){
    
        }
    
    }
    

    Then, you build a RestTemplate bean in a @Configuration class in order to manage client authorization:

    @Bean
    public RestTemplate clientAccessRestTemplate() {
        RestTemplate template = new RestTemplate();
        template.getMessageConverters().add(new FormHttpMessageConverter());
        template.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
        template.getInterceptors().add(new ClientHttpRequestInterceptor() {
    
            @Override
            public ClientHttpResponse intercept(HttpRequest request, byte[] body,
                    ClientHttpRequestExecution execution) throws IOException {
                //Intercept each of the requests performed by this template 
                //and add the client access token in the Authorization header
                HttpRequest wrapper = new HttpRequestWrapper(request);
                if (clientAccessToken != null) {
                    wrapper.getHeaders().set("Authorization",
                            "Bearer " + clientAccessToken.getToken());
                }
                return execution.execute(wrapper, body);
            }
        });
        return template;
    }
    

    Of course, you need to be sure you've got a proper clientAccessToken in the interceptor, you'll get a 401 or 403 code otherwise. Here you've got a post on how to perform this in OAuth (you don't need user/password, just client credentials).

    As a sidenote, the keycloak adapters are handy to manage some situations, but they don't provide access to all the features of keycloak, which is a way more powerful.

    0 讨论(0)
  • 2020-12-10 08:35

    KeycloakRestTemplate sends client ID, client secret, username and password to the Keycloak server. I wanted to only send client ID and secret. I created a KeycloakClientCredentialsRestTemplate subclass of OAuth2RestTemplate to do this. It uses OAuth2 support in Spring Boot to do a client credentials grant. It also takes Keycloak properties from application.properties.

    import org.springframework.security.oauth2.client.OAuth2ClientContext;
    import org.springframework.security.oauth2.client.OAuth2RestTemplate;
    import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails;
    
    public class KeycloakClientCredentialsRestTemplate extends OAuth2RestTemplate {
    
        public KeycloakClientCredentialsRestTemplate(OAuth2ProtectedResourceDetails resource,
                OAuth2ClientContext context) {
            super(resource, context);
        }
    
    }
    

    Also:

    import java.util.ArrayList;
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.security.oauth2.client.DefaultOAuth2ClientContext;
    import org.springframework.security.oauth2.client.token.grant.client.ClientCredentialsResourceDetails;
    import org.springframework.security.oauth2.common.AuthenticationScheme;
    import org.springframework.stereotype.Service;
    
    @Service
    public class KeycloakClientCredentialsConfig {
    
        @Value("${keycloak.realm}")
        private String realm;
    
        @Value("${keycloak.auth-server-url}")
        private String authServerUrl;
    
        @Value("${keycloak.resource}")
        private String clientId;
    
        @Value("${keycloak.credentials.secret}")
        private String clientSecret;
    
        @Bean
        public KeycloakClientCredentialsRestTemplate createRestTemplate() {
            return new KeycloakClientCredentialsRestTemplate(getClientCredentialsResourceDetails(),
                    new DefaultOAuth2ClientContext());
        }
    
        private ClientCredentialsResourceDetails getClientCredentialsResourceDetails() {
            String accessTokenUri = String.format("%s/realms/%s/protocol/openid-connect/token",
                authServerUrl, realm);
            List<String> scopes = new ArrayList<String>(0); // TODO introduce scopes
    
            ClientCredentialsResourceDetails clientCredentialsResourceDetails = 
                    new ClientCredentialsResourceDetails();
    
            clientCredentialsResourceDetails.setAccessTokenUri(accessTokenUri);
            clientCredentialsResourceDetails.setAuthenticationScheme(AuthenticationScheme.header);
            clientCredentialsResourceDetails.setClientId(clientId);
            clientCredentialsResourceDetails.setClientSecret(clientSecret);
            clientCredentialsResourceDetails.setScope(scopes);
    
            return clientCredentialsResourceDetails;
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题