복붙노트

[SPRING] HttpComponentsMessageSender를 사용하여 기본 Auth가있는 WebServiceTemplate

SPRING

HttpComponentsMessageSender를 사용하여 기본 Auth가있는 WebServiceTemplate

아래에 기본 인증으로 보안이 설정된 스프링 웹 서비스를 테스트하려고합니다. 이 테스트에서는 Spring의 WebServiceTemplate 클래스를 사용하여 웹 서비스 클라이언트를 작성했습니다.

템플릿의 MessageSender를 org.apache.commons.httpclient.UsernamePasswordCredentials와 함께 org.springframework.ws.transport.http.CommonsHttpMessageSender 객체 빈으로 생성하면 웹 서비스 클라이언트 호출이 웹 서비스 작업을 제대로 수행 할 수 있습니다. 코드에는 CommonsHttpMessageSender 클래스가 현재 사용되지 않으며 대신 HttpComponentsMessageSender를 사용해야한다는 경고가 강조 표시되어 있습니다.

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

이전의 비추천 클래스를 사용하는 작업 코드가 사용하는 항아리 : 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;
    }

}

미리 감사드립니다. 오후

해결법

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

    1.UsernamePasswordCredentials와 함께 HttpComponentsMessageSender를 사용하십시오. HttpComponentsMessageSender는 Spring 빈으로 생성해야하며, 수동으로 afterPropertiesSet을 호출하여 http 클라이언트를 올바르게 설정해야합니다. 이 작품은 나를 위해 :

    UsernamePasswordCredentials와 함께 HttpComponentsMessageSender를 사용하십시오. HttpComponentsMessageSender는 Spring 빈으로 생성해야하며, 수동으로 afterPropertiesSet을 호출하여 http 클라이언트를 올바르게 설정해야합니다. 이 작품은 나를 위해 :

    @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;
        }
    
    }
    
  2. ==============================

    2.이것은 org.apache.httpcomponents를 사용하여 우리 프로젝트에 대한 운동입니다 : httpclient-4.5.3, httpcore-4.4.6

    이것은 org.apache.httpcomponents를 사용하여 우리 프로젝트에 대한 운동입니다 : httpclient-4.5.3, httpcore-4.4.6

    우리는 인터셉터 헤더 RequestDefaultHeaders reqHeader = new RequestDefaultHeaders (헤더)를 생성 한 다음 CloseableHttpClient를 빌드 할 때 .addInterceptorLast (reqHeader)를 사용하여 httpClient에 추가합니다.

    구성 클래스 :

    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;
    }
    
  3. ==============================

    3.내가 사용한 한 가지 해결책은 사용자 지정 CredentialsProvider를 사용하여 사용자 지정 WebServiceMessageSender를 만드는 것입니다. 또한이 솔루션은 기본 Java 프록시 설정을 존중하는 라우트 플래너를 설정합니다.

    내가 사용한 한 가지 해결책은 사용자 지정 CredentialsProvider를 사용하여 사용자 지정 WebServiceMessageSender를 만드는 것입니다. 또한이 솔루션은 기본 Java 프록시 설정을 존중하는 라우트 플래너를 설정합니다.

    @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. ==============================

    4.결국, 현재 httpclient-4.3. +, httpcore-4.3. + 클래스를 사용하여 spring-ws-xxx.2.2.0.RELEASE의 Spring WebServiceTemplate을 사용하여 기본 인증을 작동 시키려면, preemptive 인증 인터셉터를 HttpClient (Apache HttpClient 4의 선점 기본 인증에서 @Oliv가 제안한대로). @Oliv가 지적한 것처럼이 솔루션은 모든 요청에 ​​인증을 추가합니다.

    결국, 현재 httpclient-4.3. +, httpcore-4.3. + 클래스를 사용하여 spring-ws-xxx.2.2.0.RELEASE의 Spring WebServiceTemplate을 사용하여 기본 인증을 작동 시키려면, preemptive 인증 인터셉터를 HttpClient (Apache HttpClient 4의 선점 기본 인증에서 @Oliv가 제안한대로). @Oliv가 지적한 것처럼이 솔루션은 모든 요청에 ​​인증을 추가합니다.

    Spring WebServiceTemplate을 구성하는 가장 좋은 방법인지 확실하지 않지만 HttpClient의 HttpClientContext 객체에 직접 액세스하지 않고 선점 인증을 사용할 수있는 유일한 방법입니다. 더 간단하고 좋은 답변을드립니다.

    인터셉터 코드 :

    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);
            }
        }
    }
    
  5. ==============================

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

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

    스프링 문서에 따라 :

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

  6. from https://stackoverflow.com/questions/24609751/webservicetemplate-with-basic-auth-using-httpcomponentsmessagesender by cc-by-sa and MIT license