2

현재 기본 인증 아래에 보안이 설정된 스프링 웹 서비스를 테스트하려고합니다. 이 테스트에서는 Spring의 WebServiceTemplate 클래스를 사용하여 웹 서비스 클라이언트를 작성했습니다. 나는 클라이언트가 작동하지만, 코드가 경고가 CommonsHttpMessageSender 클래스는 현재 사용되지 않으며 것을 말하는 강조된했다 org.apache.commons.httpclient.UsernamePasswordCredentialsorg.springframework.ws.transport.http.CommonsHttpMessageSender 객체 빈으로 템플릿의 MessageSender을 만들 때HttpComponentsMessageSender를 사용하여 Basic Auth가있는 WebServiceTemplate

내 웹 서비스 클라이언트는 괜찮 웹 서비스 작업에 대한 호출 대신 HttpComponentsMessageSender을 사용해야합니다.

새로운 HttpComponentsMessageSender 클래스를 사용하여 클라이언트의 WebServiceTemplate을 다시 구성하려고 시도했지만 올바르게 설정된 기본 인증 파트를 가질 수 없습니다. 새로운 HttpComponentsMessageSender 클래스의 경우 org.apache.http.auth.UsernamePasswordCredentials 클래스를 사용하여 자격 증명을 만들었지 만 웹 서비스를 호출하면 자격 증명이 요청과 함께 사용할 수없는 것 같습니다. 요청을 인증하는 등의 새로운 클래스를 사용하는 WebServiceTemplate 클라이언트의 작동 예제가 있습니까?

이전의 비추천 클래스를 사용하는 코드가 사용하는 jar는 commons-httpclient-3.1, spring-ws-core-2.2.0.RELEASE입니다.

최신 클래스의 작동하지 않는 코드가 사용하는 점은 httpclient-4.3.4, httpcore-4.3.2, spring-ws-core-2.2.0.RELEASE입니다.

테스트 구성은 비 작동 코드의 약자로 :

사전에
package com.company.service.a.ws.test.config; 

import java.io.IOException; 

import org.apache.http.HttpException; 
import org.apache.http.HttpRequest; 
import org.apache.http.HttpRequestInterceptor; 
import org.apache.http.auth.AuthScope; 
import org.apache.http.auth.UsernamePasswordCredentials; 
import org.apache.http.client.CredentialsProvider; 
import org.apache.http.client.HttpClient; 
import org.apache.http.client.config.RequestConfig; 
import org.apache.http.impl.client.BasicCredentialsProvider; 
import org.apache.http.impl.client.HttpClientBuilder; 
import org.apache.http.impl.client.HttpClients; 
import org.apache.http.protocol.HTTP; 
import org.apache.http.protocol.HttpContext; 
import org.apache.logging.log4j.LogManager; 
import org.apache.logging.log4j.Logger; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.context.annotation.Bean; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.context.annotation.Configuration; 
import org.springframework.context.annotation.DependsOn; 
import org.springframework.context.annotation.PropertySource; 
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; 
import org.springframework.oxm.jaxb.Jaxb2Marshaller; 
import org.springframework.ws.client.core.WebServiceTemplate; 
import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; 
import org.springframework.ws.transport.http.HttpComponentsMessageSender; 

@PropertySource("classpath:/${environment}-use-case-data.properties") 
@ComponentScan(basePackages = "com.company.service.a.ws.test") 
@Configuration 
public class TestConfig { 

    @Value("${ws.url}") 
    private String wsUrl; 

    @Value("${ws.username}") 
    private String username; 

    @Value("${ws.password}") 
    private String password; 

    private static final Logger logger = LogManager.getLogger(); 

    @Bean 
    public SaajSoapMessageFactory messageFactory() { 
     return new SaajSoapMessageFactory(); 
    } 

    @Bean 
    public Jaxb2Marshaller marshaller() { 
     Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); 
     marshaller.setContextPath("com.company.service.a.ws.model.data"); 
     return marshaller; 
    } 

    @Bean RequestConfig requestConfig() { 

     RequestConfig requestConfig = RequestConfig.custom() 
       .setAuthenticationEnabled(true) 
       .build(); 
     return requestConfig; 
    } 

    @Bean 
    @DependsOn(value = "propertyConfigurer") 
    public UsernamePasswordCredentials credentials() { 

     logger.debug("creating credentials for username: {} passowrd={}", 
       username, password); 

     UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(
       username, password); 

     return credentials; 
    } 

    @Bean 
    public CredentialsProvider credentialsProvider() { 
     CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); 
     credentialsProvider.setCredentials(AuthScope.ANY, credentials()); 
     return credentialsProvider; 
    } 

    private static class ContentLengthHeaderRemover implements HttpRequestInterceptor{ 

     @Override 
     public void process(HttpRequest request, HttpContext context) 
       throws HttpException, IOException { 

      // fighting org.apache.http.protocol.RequestContent's 
      // ProtocolException("Content-Length header already present"); 
      request.removeHeaders(HTTP.CONTENT_LEN); 
     } 
    } 

    @Bean 
    public HttpComponentsMessageSender messageSender() { 

     RequestConfig requestConfig = RequestConfig.custom() 
       .setAuthenticationEnabled(true) 
       .build(); 

     HttpClientBuilder httpClientBuilder = HttpClients.custom(); 

     HttpClient httpClient = httpClientBuilder 
       .addInterceptorFirst(new ContentLengthHeaderRemover()) 
       .setDefaultRequestConfig(requestConfig) 
       .setDefaultCredentialsProvider(credentialsProvider())    
       .build(); 

     HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender(httpClient); 
     return messageSender; 
    } 

    @Bean(name = "propertyConfigurer") 
    public static PropertySourcesPlaceholderConfigurer propertyConfigurer() { 
     PropertySourcesPlaceholderConfigurer configurer = 
       new PropertySourcesPlaceholderConfigurer(); 

     return configurer; 
    } 

    @Bean 
    public WebServiceTemplate webServiceTemplate() { 

     logger.debug("creating webServiceTemplate to url: {}", wsUrl); 

     WebServiceTemplate webServiceTemplate = new WebServiceTemplate(messageFactory()); 
     webServiceTemplate.setDefaultUri(wsUrl); 
     webServiceTemplate.setMarshaller(marshaller()); 
     webServiceTemplate.setUnmarshaller(marshaller()); 
     webServiceTemplate.setMessageSender(messageSender()); 
     return webServiceTemplate; 
    } 

} 

감사합니다, 결국 오후

+0

포스트 귀하의 구성. 동일한 설정을 문제없이 사용하므로 설정이 잘못되었다고 생각됩니다. –

+0

안녕하세요 @ M.Deinum, 나는 현재 작동하지 않는 코드를 나타냅니다로서 웹 서비스 클라이언트의 봄 콩 설정 코드를 추가했습니다. 내 클라이언트는 템플릿에서 autowires하고 단지'Object response = webServiceTemplate.marshalSendAndReceive (request);를 호출한다. 여기서 요청은 단지 웹 서비스의 페이로드 POJO이다. –

+1

아마도 http://stackoverflow.com/questions/20914311/httpclientbuilder-basic-auth를 실행하고있을 것입니다. 우리는 동일한 스택을 사용하지만 4.2 버전을 사용하고 4.3은 아닌 것을 생각했습니다. –

답변

0

, httpclient-4.3.+ 현재 사용 spring-ws-xxx.2.2.0.RELEASE에 봄 WebServiceTemplate과 기본 인증 작업을 만들려면 , httpcore-4.3.+ 클래스, HttpClient에 선점 인증 인터셉터를 추가했습니다 (Preemptive Basic authentication with Apache HttpClient 4의 @Oliv에서 제안한대로). @Oliv가 지적한 것처럼이 솔루션은 모든 요청에 ​​인증을 추가합니다.

나는 여전히이 봄 WebServiceTemplate를 구성하는 가장 좋은 방법입니다 확실하지 오전하지만 내가 (지금까지)을 HttpClientHttpClientContext 오브젝트에 직접 액세스하지 않고 선제 인증을 가능하게 발견하는 유일한 방법입니다. 내가 사용하고

private static class PreemptiveAuthInterceptor implements HttpRequestInterceptor { 

    public void process(final HttpRequest request, final HttpContext context) 
      throws HttpException, IOException { 

     AuthState authState = (AuthState) context.getAttribute(
       HttpClientContext.TARGET_AUTH_STATE); 

     // If no auth scheme is avaialble yet, initialize it preemptively 
     if (authState.getAuthScheme() == null) { 

      CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
        HttpClientContext.CREDS_PROVIDER); 

      HttpHost targetHost = (HttpHost) context.getAttribute(
        HttpCoreContext.HTTP_TARGET_HOST); 

      Credentials creds = credsProvider.getCredentials(
        new AuthScope(targetHost.getHostName(), targetHost.getPort())); 

      if (creds == null) { 
       throw new HttpException("no credentials available for preemptive " 
         + "authentication"); 
      } 

      authState.update(new BasicScheme(), creds); 
     } 
    } 
} 
+0

"HttpClient의 HttpClientContext 개체에 직접 액세스 할 수 없다"는 의미가 무엇인지, 아니면 왜 원하지 않는지 확실하지 않습니다. 단순히 HttpClient를 꾸미고 각 실행 호출에서 변경된 컨텍스트를 전달하지 않는 이유는 무엇입니까? – kulatamicuda

1

하나의 솔루션은 사용자 정의 CredentialsProvider와 사용자 정의 WebServiceMessageSender를 만드는 것입니다 : 모든 간단한 더 나은 답변 나는

인터셉터 코드 ... 매우 환영합니다. 이 솔루션은 또한 기본 자바 프록시 설정을 존중하는 라우트 플래너를 설정합니다.

@Configuration 
public class WebServiceConfiguration { 

    @Bean 
    public WebServiceMessageSender webServiceMessageSender(@Value("${endpoint.uri}") endpointUri, 
                  @Value("${endpoint.username}") String username, 
                  @Value("${endpoint.password}") String password) throws Exception { 
     SystemDefaultRoutePlanner routePlanner = new SystemDefaultRoutePlanner(
      ProxySelector.getDefault()); 
     BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider(); 
     credentialsProvider.setCredentials(new AuthScope(endpointUri.getHost(), endpointUri.getPort(), ANY_REALM, ANY_SCHEME), new UsernamePasswordCredentials(username, password);); 
     CloseableHttpClient httpclient = HttpClients.custom() 
      .setRoutePlanner(routePlanner) 
      .addInterceptorFirst(new HttpComponentsMessageSender.RemoveSoapHeadersInterceptor()) 
      .setDefaultCredentialsProvider(credentialsProvider) 
      .build(); 

     return new HttpComponentsMessageSender(httpclient); 
    } 
} 
4

UsernamePasswordCredentials에 사용 HttpComponentsMessageSender. HttpComponentsMessageSender은 스프링 빈으로 만들어야하며, http 클라이언트를 올바르게 설정하려면 afterPropertiesSet을 수동으로 호출해야합니다. 이 작품은 나를 위해 :

@Configuration 
public class WsClientConfiguration { 


    @Bean 
    public ESignatureProcessorClient eSignatureProcessorClient() { 
     ESignatureProcessorClient client = new ESignatureProcessorClient(); 
     client.setWebServiceTemplate(mwWebServiceTemplate()); 
     return client; 
    } 

    @Bean 
    public WebServiceTemplate mwWebServiceTemplate() { 
     Jaxb2Marshaller marshaller = new Jaxb2Marshaller(); 
     marshaller.setContextPath("cz.csas.services.esignatureprocessor.v02_02"); 
     WebServiceTemplate template = new WebServiceTemplate(marshaller, marshaller); 
     template.setDefaultUri("https://osb-st2.vs.csin.cz:5001/CSMW/WS_MW_ESignatureProcessor_v02_02"); 
     template.setMessageSender(defaultMwMessageSender()); 
     return template; 
    } 

    @Bean 
    public HttpComponentsMessageSender defaultMwMessageSender() { 
     HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender(); 
     messageSender.setCredentials(new UsernamePasswordCredentials("user", "password")); 
     return messageSender; 
    } 

} 
0

스레드는 오래되었지만 요약입니다.

봄 문서 당으로

:

UsernamePasswordCredentials 및 HttpComponentsMessageSender 봄 콩이어야한다. 따라서 빈을 정의하고 삽입하십시오. 문제를 해결해야합니다.

1

이것은 우리의 프로젝트는 org.apache.httpcomponents을 사용하는 운동입니다 :

httpcore-4.4.6 우리는 요격 헤더 RequestDefaultHeaders reqHeader = new RequestDefaultHeaders(headers)을 만들고 CloseableHttpClient을 구축 할 때 다음 .addInterceptorLast(reqHeader)를 사용하여 세션 객체에 추가 httpclient-4.5.3,

구성 클래스 :

import org.apache.http.message.BasicHeader; 
import org.apache.http.impl.client.CloseableHttpClient; 
import org.apache.http.Header; 
import org.apache.http.client.protocol.RequestDefaultHeaders; 


@Bean 
HttpClient createHttpClient() { 
    List<Header> headers = new ArrayList<>(); 
    BasicHeader authHeader = new BasicHeader("Authorization", "Basic " + base64authUserPassword()); 
    headers.add(authHeader); 
    // add more header as more as needed 

    RequestDefaultHeaders reqHeader = new RequestDefaultHeaders(headers); 

    CloseableHttpClient httpClient = 
     HttpClients.custom() 
      .addInterceptorFirst(new HttpComponentsMessageSender.RemoveSoapHeadersInterceptor()) 
      .addInterceptorLast(reqHeader) 
      .build(); 
    return httpClient; 
} 

@Bean 
public HttpComponentsMessageSender defaultMyMessageSender() 
     throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException { 

    HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender(createHttpClient()); 
    //messageSender.setCredentials(credentials()); 
    return messageSender; 
} 

@Bean 
WebServiceTemplate webServiceTemplate() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException{ 

    WebServiceTemplate wsTemplate = new WebServiceTemplate(); 
    wsTemplate.setDefaultUri(endpointURI); 
    wsTemplate.setMessageSender(defaultMyMessageSender()); 

    return wsTemplate; 
}