[SPRING] Keycloak 스프링 보안 클라이언트 자격 증명 부여
SPRINGKeycloak 스프링 보안 클라이언트 자격 증명 부여
하나의 키 클로크 클라이언트가 다른 키 클로크 클라이언트와 통신하는 곳에서 KecloakRestTemplate을 사용할 수 있습니다. 그러나 첫 번째 키 클로크 클라이언트에 로그인 한 경우에만 작동합니다. 즉, 클라이언트 ID, 클라이언트 암호, 사용자 이름, 암호를 keycloak 서버에 보냅니다. 첫 번째 클라이언트에서 사용자와 암호로 인증하지 않은 경우 "인증 된 원칙이 없기 때문에 권한 헤더를 설정할 수 없습니다"라는 메시지가 나타납니다. 하지만 첫 번째 클라이언트 (클라이언트 자격증 부여)에 서비스 계정을 사용하도록 keycloak을 구성 했으므로 사용자 / 암호를 사용해서는 안되며 클라이언트 ID / 비밀에만 의존해야합니다. 이것은 OAuth 2 스펙의 버그 / 위반입니까?
해결법
-
==============================
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.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로서 키 클로크 어댑터는 어떤 상황을 관리하는 데 편리하지만 키 클로크의 모든 기능에 대한 액세스를 제공하지는 않습니다. 이는 더 강력한 방법입니다.
from https://stackoverflow.com/questions/46073485/keycloak-spring-security-client-credential-grant by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] 빈 작성시 빌드시 예외가 발생 함 (0) | 2019.02.26 |
---|---|
[SPRING] RestTemplate을 사용하는 Spring Boot 멀티 파트 콘텐츠 유형 HTTP 요청 (0) | 2019.02.26 |
[SPRING] GAE에서 작동하지 않는 Spring Autowiring (0) | 2019.02.26 |
[SPRING] Spring Security는 로그인 후 항상 403 accessDeniedPage를 반환합니다 [duplicate] (0) | 2019.02.26 |
[SPRING] Eclipse에서 모든 JUnit 테스트를 독립적으로 실행하고 매번 Spring 컨텍스트를 다시로드합니다. (0) | 2019.02.26 |