반응형
Spring framework의 KerberosRestTemplate를 사용해서 HDFS REST API를 통해 파일 다운로드, 업로드하는 예제이다.
일단 KerberosRestTemplate을 사용하기 위한 의존성 설정이 필요하다.
<dependency>
<groupId>org.springframework.security.kerberos</groupId>
<artifactId>spring-security-kerberos-client</artifactId>
<version>1.0.1.RELEASE</version>
</dependency>
예제 코드
import org.apache.commons.lang3.BooleanUtils;
import org.apache.http.auth.AuthSchemeProvider;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.Credentials;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.config.Lookup;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.impl.auth.SPNegoSchemeFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.security.kerberos.client.KerberosRestTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriComponentsBuilder;
import java.io.File;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.Principal;
import java.util.List;
@Component
public class HdfsService {
private String principal = "userId@domain.com"; // 유저 정보
private String keytab = "/Users/user/my-keytab.keytab"; // keytab 파일 경로
private String restApiUrl = "https://hdfs-rest-api-url/webhdfs/v1"; // REST API URL
private KerberosRestTemplate kerberosRestTemplate = createKerberosRestTemplate();
private KerberosRestTemplate createKerberosRestTemplate() {
HttpClientBuilder builder = HttpClientBuilder.create();
builder.setDefaultAuthSchemeRegistry(createAuthSchemeRegistry());
builder.setDefaultCredentialsProvider(createCredentialsProvider());
CloseableHttpClient httpClient = builder.build();
KerberosRestTemplate restTemplate = new KerberosRestTemplate(keytab, principal, httpClient);
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
factory.setConnectTimeout(3000);
factory.setReadTimeout(3000);
factory.setBufferRequestBody(Boolean.TRUE);
restTemplate.setRequestFactory(factory);
return restTemplate;
}
private Lookup<AuthSchemeProvider> createAuthSchemeRegistry() {
return RegistryBuilder.<AuthSchemeProvider>create()
.register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(true)).build();
}
private BasicCredentialsProvider createCredentialsProvider() {
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new Credentials() {
@Override
public Principal getUserPrincipal() {
return null;
}
@Override
public String getPassword() {
return null;
}
});
return credentialsProvider;
}
public File downloadFile(String sourcePath, String downloadPath) {
URI uri = UriComponentsBuilder.fromHttpUrl(restApiUrl)
.path(sourcePath)
.queryParam("op", "OPEN")
.build().toUri();
return kerberosRestTemplate.execute(uri, HttpMethod.GET, null, response -> {
Files.copy(response.getBody(), Paths.get(downloadPath));
return new File(downloadPath);
});
}
public void uploadFile(String sourcePath, String destinationPath, boolean isOverwrite) {
URI locationGetUri = UriComponentsBuilder.fromHttpUrl(restApiUrl)
.path(destinationPath)
.queryParam("op", "CREATE")
.queryParam("overwrite", BooleanUtils.toStringTrueFalse(isOverwrite))
.buildAndExpand(destinationPath).toUri();
ResponseEntity<Void> locationResponse = kerberosRestTemplate.exchange(locationGetUri, HttpMethod.PUT, null, Void.class);
List<String> locationHeaderValue = locationResponse.getHeaders().get("Location");
URI uploadLocationUri = UriComponentsBuilder.fromHttpUrl(locationHeaderValue.get(0)).build().toUri();
HttpEntity<FileSystemResource> requestEntity = new HttpEntity<>(new FileSystemResource(sourcePath));
kerberosRestTemplate.exchange(uploadLocationUri, HttpMethod.PUT, requestEntity, Void.class);
}
}
파일을 업로드할 때는 가이드에 따르면 두 단계로 나누어 처리해야 한다.
첫번째 요청에서는 데이터를 올릴 데이터노드 URL을 받아오고 (Location 헤더를 통해 전달)
두번째 요청에서는 전단계에서 받은 URL에 실제 파일 데이터를 보낸다.
그리고 이건 내가 테스트해본 HDFS의 특성일 수도 있는데,
다운로드든 업로드든 내부적으로 처리될 때 첫번째 요청에서는 권한없음(http status 401)으로 처리되고
재차 요청이 되면서 그 때 커버로스 인증도 통과되면서 정상적으로 요청이 처리되었다.
그런데 파일 업로드의 두번째 단계 요청에서는 실제 파일 데이터를 보내다보니
재차 요청할 때 데이터가 버퍼링이 되어있지 않으면 요청을 다시 할 수 없어서 실패하는 현상이 있었다.
그래서 factory.setBufferRequestBody(Boolean.TRUE); 를 통해서 버퍼링이 가능하게하니 성공하였다.
반응형
'개발 > Spring' 카테고리의 다른 글
Spring Controller에서 파라미터 검증 방법들 (0) | 2020.06.18 |
---|---|
스프링 @Async를 통한 비동기 처리 및 설정값 (0) | 2020.06.03 |
MyBatis cannot change the ExecutorType when there is an existing transaction 오류 (0) | 2018.04.14 |
Spring Batch skip 로직 동작 방식 (0) | 2017.04.24 |
Spring Batch commit-interval에 대한 정리 (0) | 2016.12.23 |