現在、基本認証で保護されているSpringWebサービスをテストしようとしています。これらのテストでは、SpringのWebServiceTemplate
クラスを使用してWebサービスクライアントを作成しました。
テンプレートのMessageSenderをorg.springframework.ws.transport.http.CommonsHttpMessageSender
でorg.Apache.commons.httpclient.UsernamePasswordCredentials
オブジェクトBeanとして作成すると、WebサービスクライアントのWebサービス呼び出しは正常に機能します。クライアントは機能しますが、コードにはCommonsHttpMessageSender
クラスは非推奨になり、代わりにHttpComponentsMessageSender
を使用する必要があります。
新しいWebServiceTemplate
クラスを使用して動作するようにクライアントのHttpComponentsMessageSender
を再構成しようとしましたが、基本認証部分を正しく構成できません。新しいHttpComponentsMessageSender
クラスの場合、org.Apache.http.auth.UsernamePasswordCredentials
クラスを使用して資格情報を作成しましたが、Webサービスを呼び出すと、要求で資格情報を使用できないようです。リクエストの認証などにこれらの新しいクラスを使用するWebServiceTemplateクライアントの実用的な例はどこにありますか?
古い非推奨クラスの作業コードが使用するjar:commons-httpclient-3.1
、spring-ws-core-2.2.0.RELEASE
。
新しいクラスで動作しないコードが使用するjar: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;
}
}
よろしくお願いします、PM
HttpComponentsMessageSender
をUsernamePasswordCredentials
と一緒に使用します。 HttpComponentsMessageSender
をSpringBeanとして作成するか、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;
}
}
これは、_org.Apache.httpcomponents
_を使用したプロジェクトのトレーニングです:_httpclient-4.5.3
_、_httpcore-4.4.6
_
インターセプターヘッダーRequestDefaultHeaders reqHeader = new RequestDefaultHeaders(headers)
を作成し、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;
}
_
私が使用したソリューションの1つは、カスタム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);
}
}
最後に、現在のspring-ws-xxx.2.2.0.RELEASE
、httpclient-4.3.+
クラスを使用してhttpcore-4.3.+
のSpringWebServiceTemplate
で基本認証を機能させるために、HttpClient
にプリエンプティブ認証インターセプターを追加しました(@で提案されています)。 Oliv in Apache HttpClient 4を使用したプリエンプティブ基本認証 )。 @Olivが指摘しているように、このソリューションは、行われたすべての要求に認証を追加することに注意してください。
これがSpringWebServiceTemplate
を構成するための最良の方法であるかどうかはまだわかりませんが、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);
}
}
}
スレッドは古いですが、要約します。
春のドキュメントによると:
UsernamePasswordCredentialsとHttpComponentsMessageSenderはSpringBeanである必要があります。したがって、Beanを定義して注入します。それは問題を解決するはずです。