2017-09-06 121 views
0

我可以使用KecloakRestTemplate,其中一个keycloak客户端正在与另一个keycloak客户端进行通信。然而,只有当我登录到第一个keycloak客户端时它才有效,即它将客户端ID,客户端密码,用户名,密码发送到keycloak服务器。如果我没有在第一个客户端上使用用户和密码进行身份验证,则会收到“无法设置授权标头,因为没有经过身份验证的原则”。但是我已经配置了keycloak为第一个客户端使用服务帐户(Client Credential Grant),因此我不应该使用用户/密码,并且应该仅依赖于客户端ID /密码。这是否是OAuth 2规范的错误/偏差?Keycloak弹簧安全客户端凭证授权

回答

0

对于基于微服务架构的应用程序,我使用用户和客户端帐户。我猜Spring安全适配器只处理用户相关的东西(我使用的版本,至少是2.2.1)。我所做的是另一个RestTemplate,我为了作为客户访问资源而自行处理。

举个例子:

@Service 
public class RemoteAccessService{ 

    //Manages user access 
    private KeycloakRestTemplate userAccessRestTemplate; 

    //Manages client access 
    private RestTemplate clientAccessRestTemplate; 

    public RemoteAccessService(KeycloakRestTemplate userAccessRestTemplate, 
     @Qualifier("clientAccessRestTemplate") RestTemplate clientAccessRestTemplate;){ 

    } 

} 

然后,建立一个@ConfigurationRestTemplate豆,以管理客户端授权:

@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; 
} 

当然,你需要确保你”已经在拦截器中获得了正确的clientAccessToken,否则你将得到401或403代码。在这里,您已经获得了关于如何在OAuth中执行此操作的post(您不需要用户/密码,只需要客户端凭据)。

作为旁注,keycloak适配器可以方便地管理某些情况,但它们不提供对keycloak所有功能的访问,这是一种更强大的方式。

0

KeycloakRestTemplate将客户端ID,客户端密码,用户名和密码发送到Keycloak服务器。我只想发送客户端ID和密码。我创建了一个KeycloakClientCredentialsRestTemplate子类OAuth2RestTemplate来做到这一点。它在Spring Boot中使用OAuth2支持来执行客户端凭据授权。它还需要application.properties的Keycloak属性。

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); 
    } 

} 

另外:

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; 
    } 

}