2016-11-16 4 views
0

이것은 다소 이상하며이 문제의 원인과 해결 방법을 찾기에 충분한 연구를 수행했습니다. 내 목표는 로그인이 필요한 보안 URL에서 zip 파일을 다운로드하는 것입니다. 버전 4.3.6의 apache httpClient maven 종속성을 사용하면 모든 것이 완벽하게 작동합니다. 그러나 aws-sdk-java-core maven 종속성에도 httpclient 종속성이 있고 v4.3.6을 사용하면 aws-sdk-java가 NoSuchMethod 런타임 예외에 대해 불만을 나타 내기 때문에이 버전을 사용할 수 없습니다. 이 문제를 이해합니다. 그 이유는 아파치 httpclient v4.3.6 의존성은 aws-sdk-java-core 의존성이 사용하는 버전 (4.5.1)보다 maven 의존성 트리에서 더 가깝기 때문이다. 어쨌든, 필자는 모든 것에 대해 Maven 종속성의 한 버전으로 작업하고 동일한 병의 여러 버전을 사용하지 않아야한다고 확신하기 때문에 이것에 대한 자세한 내용을 잘라 낼 것입니다. 원래 질문으로 돌아 가기. v4.3.6을 사용할 수 없기 때문에, v4.5.1을 사용하도록 코드를 말했고, 파일 다운로드 코드가 문제를 일으키기 시작했다고합니다. httpclient v4.5.1을 사용하면 요청한 https url에있는 zip 파일을 제공하지 않고 다음과 같은 html 콘텐츠를 제공합니다.zip 파일 다운로드는 apache httpclient v4.3.6에서 작동하지만 다른 버전에서는 실패합니다.

<html> 
<HEAD><META HTTP-EQUIV='PRAGMA' CONTENT='NO-CACHE'><META HTTP-EQUIV='CACHE-  
CONTROL' CONTENT='NO-CACHE'> 
<TITLE>SAML 2.0 Auto-POST form</TITLE> 
</HEAD> 
<body onLoad="document.forms[0].submit()"> 
<NOSCRIPT>Your browser does not support JavaScript. Please click the  
'Continue' button below to proceed. <br><br> 
</NOSCRIPT> 
<form action="https://githubext.deere.com/saml/consume" method="POST"> 
<input type="hidden" name="SAMLResponse" value="PFJlc3BvbnNlIHhtbG5zPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIERl"> 
<input type="hidden" name="RelayState" value="2F1HpzrUy5FdX"> 
<NOSCRIPT><INPUT TYPE="SUBMIT" VALUE="Continue"></NOSCRIPT> 
</form> 
</body> 
</html> 

v4.3.6을 사용하면 응답에서 예상 한 응답으로 zip 파일을 제공합니다. 더 많은 코드를 추가하여이 html 콘텐츠를 수동으로 제출하려했지만 응답은 그대로입니다. 파일 다운로드를위한 원본 코드가 아래에 나와 있습니다.

@Component 
public class FileDAO { 

    public static void main(String args[]) throws Exception{ 
     new FileDAO().loadFile("https://some_url.domain.com/zipball/master","myfile.zip"); 
    } 


    public String loadFile(String url, String fileName) throws ClientProtocolException, IOException { 

     HttpClient client = login(); 
     HttpResponse response = client.execute(new HttpGet(url)); 
     int statusCode = response.getStatusLine().getStatusCode(); 
     if (statusCode == 200) { 
      String unzipToFolderName = fileName.replace(".", "_"); 
      FileOutputStream outputStream = new FileOutputStream(new File(fileName)); 
      writeToFile(outputStream, response.getEntity().getContent());    
      return unzipToFolderName; 
     } else { 
      throw new RuntimeException("error downloading file, HTTP Status code: " + statusCode); 
     } 
    } 

    private void writeToFile(FileOutputStream outputStream, InputStream inputStream) { 
     try { 
      int read = 0; 
      byte[] bytes = new byte[1024]; 
      while ((read = inputStream.read(bytes)) != -1) { 
       outputStream.write(bytes, 0, read); 
      } 
     } catch (Exception ex) { 
      throw new RuntimeException("error writing zip file, error message : " + ex.getMessage(), ex); 
     } finally { 
      try { 
       outputStream.close(); 
       inputStream.close(); 
      } catch (Exception ex) {} 
     } 
    } 

    private HttpClient login() throws IOException { 
     HttpClient client = getHttpClient(); 

     HttpResponse response = client.execute(new HttpGet("https://some_url.domain.com")); 
     String responseBody = EntityUtils.toString(response.getEntity()); 
     Document doc = Jsoup.parse(responseBody); 
     org.jsoup.select.Elements inputs = doc.getElementsByTag("input"); 
     int statusCode = response.getStatusLine().getStatusCode(); 
     if (statusCode == 200) { 
      HttpPost httpPost = new HttpPost("https://some_url.domain.com/saml/consume"); 
      List<NameValuePair> data = new ArrayList<NameValuePair>(); 
      data.add(new BasicNameValuePair("SAMLResponse", doc.select("input[name=SAMLResponse]").val())); 
      data.add(new BasicNameValuePair("RelayState", doc.select("input[name=RelayState]").val())); 
      httpPost.setEntity(new UrlEncodedFormEntity(data)); 
      HttpResponse logingResponse = client.execute(httpPost); 
      int loginStatusCode = logingResponse.getStatusLine().getStatusCode(); 
      if (loginStatusCode != 302) { 
       throw new RuntimeException("clone repo dao. error during login, HTTP Status code: " + loginStatusCode); 
      } 
     } 
     return client; 
    } 

    private HttpClient getHttpClient() { 
     CredentialsProvider provider = new BasicCredentialsProvider(); 
     UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("userId", "password"); 
     provider.setCredentials(AuthScope.ANY, credentials); 
     return HttpClientBuilder.create().setDefaultCredentialsProvider(provider).build(); 
    } 
} 

4.3.6 이외의 httpclient 버전의 apache에 문제가있는 것으로 판단됩니다. 같은 코드가 4.3.6 이상에서는 작동하지만 4.3.6 이상에서는 작동하지 않습니다. 도움이 정말 감사합니다. 다들 감사 해요.

답변

1

문제가 해결되었습니다. 심각한 디버깅 로그와 함께 아파치 httpclient 문서를 살펴본 후에이 문제를 해결할 수있었습니다. v4.3.6 용과 v4.5.2 용으로 두 개의 서버 로그를 작성해야했습니다. 나는 서버 로그를 비교하기 시작했고 범인은 쿠키 유형이라는 것을 알았다. 이전 버전의 쿠키 유형이 (자동으로) BEST_MATCH로 구성되어 작동했습니다. 그러나 v4.5.2의 경우 BEST_MATCH 쿠키 유형은 apache에서 사용되지 않습니다. 좀 더 많은 코드를 추가 한 후에 쿠키 설정을 시도했지만 서버 응답에서 보낸 쿠키가 클라이언트 코드에서 구성한 DEFAULT 쿠키 유형과 일치하지 않았습니다. 결과적으로 쿠키가 제대로 설정되지 않아서 응답이 zip 파일 대신 SAML 응답 (로그인 페이지 다시)을 반환하는 이유입니다.

기본 : 기본 쿠키 정책이 함께 전송 쿠키의 속성을 기반으로 하나 RFC 2965, RFC 2109 또는 Netscape 초안을 준수 구현 집어 합성 정책

아파치 cookie spec 쿠키 사양이 말한다 HTTP 응답 (예 : 버전 특성, 현재는 사용되지 않음) 이 정책은 HttpClient의 다음 부 릴리스에서 표준 (RFC 6265 준수) 구현을 위해 더 이상 사용되지 않습니다. 표준 엄격한 : 구문 및 RFC 6265에 의해 정의 된 잘 행동 프로파일의 의미 준수 상태 관리 정책, 섹션 4

I 업데이트 쿠키 구성 STANDARD_STRICT에 모드와 모든 최신 버전으로 작업을 시작했다 4.5.2.

private CloseableHttpClient getHttpClient() { 
    CredentialsProvider provider = new BasicCredentialsProvider(); 
    UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(gitUserId, gitPassword); 
    provider.setCredentials(AuthScope.ANY, credentials); 
    RequestConfig config = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD_STRICT).build(); 
    return HttpClientBuilder.create().setDefaultCredentialsProvider(provider).setDefaultRequestConfig(config).setRedirectStrategy(new LaxRedirectStrategy()).build(); 
} 
: 여기

업데이트 된 getHttpClient() 방법
관련 문제