복붙노트

[SPRING] Keycloak 스프링 보안 클라이언트 자격 증명 부여

SPRING

Keycloak 스프링 보안 클라이언트 자격 증명 부여

하나의 키 클로크 클라이언트가 다른 키 클로크 클라이언트와 통신하는 곳에서 KecloakRestTemplate을 사용할 수 있습니다. 그러나 첫 번째 키 클로크 클라이언트에 로그인 한 경우에만 작동합니다. 즉, 클라이언트 ID, 클라이언트 암호, 사용자 이름, 암호를 keycloak 서버에 보냅니다. 첫 번째 클라이언트에서 사용자와 암호로 인증하지 않은 경우 "인증 된 원칙이 없기 때문에 권한 헤더를 설정할 수 없습니다"라는 메시지가 나타납니다. 하지만 첫 번째 클라이언트 (클라이언트 자격증 부여)에 서비스 계정을 사용하도록 keycloak을 구성 했으므로 사용자 / 암호를 사용해서는 안되며 클라이언트 ID / 비밀에만 의존해야합니다. 이것은 OAuth 2 스펙의 버그 / 위반입니까?

해결법

  1. ==============================

    1.KeycloakRestTemplate은 클라이언트 ID, 클라이언트 암호, 사용자 이름 및 암호를 Keycloak 서버에 보냅니다. 고객 ID와 비밀 정보 만 보내고 싶었습니다. 이 작업을 수행하기 위해 OAuth2RestTemplate의 KeycloakClientCredentialsRestTemplate 하위 클래스를 만들었습니다. Spring Boot에서 OAuth2 지원을 사용하여 클라이언트 자격 증명 부여를 수행합니다. 또한 application.properties에서 Keycloak 속성을 사용합니다.

    KeycloakRestTemplate은 클라이언트 ID, 클라이언트 암호, 사용자 이름 및 암호를 Keycloak 서버에 보냅니다. 고객 ID와 비밀 정보 만 보내고 싶었습니다. 이 작업을 수행하기 위해 OAuth2RestTemplate의 KeycloakClientCredentialsRestTemplate 하위 클래스를 만들었습니다. 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;
        }
    
    }
    
  2. ==============================

    2.내 마이크로 서비스 아키텍처 기반 응용 프로그램의 경우 사용자 계정과 서비스 계정을 모두 사용하고 있습니다. 내 생각에 스프링 보안 어댑터는 사용자 관련 항목 (적어도 2.2.1은 사용중인 버전) 만 처리합니다. 내가하는 일은 또 다른 RestTemplate을 가지고, 클라이언트로서 리소스에 액세스하기 위해 자신을 처리하는 것입니다.

    내 마이크로 서비스 아키텍처 기반 응용 프로그램의 경우 사용자 계정과 서비스 계정을 모두 사용하고 있습니다. 내 생각에 스프링 보안 어댑터는 사용자 관련 항목 (적어도 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;){
    
        }
    
    }
    

    그런 다음 클라이언트 인증을 관리하기 위해 @Configuration 클래스에 RestTemplate 빈을 빌드합니다.

    @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에서이 작업을 수행하는 방법에 대한 게시물이 있습니다 (사용자 / 비밀번호, 클라이언트 자격 증명이 필요하지 않음).

    sidenote로서 키 클로크 어댑터는 어떤 상황을 관리하는 데 편리하지만 키 클로크의 모든 기능에 대한 액세스를 제공하지는 않습니다. 이는 더 강력한 방법입니다.

  3. from https://stackoverflow.com/questions/46073485/keycloak-spring-security-client-credential-grant by cc-by-sa and MIT license