将动态 HTTP Headers 添加到 WS 出站网关
Adding dynamic HTTP Headers to WS Outbound Gateway
我想使用一些自定义 HTTP Headers 来丰富我的 HTTP/SOAP 请求(使用 WS 出站网关发送),具体取决于 Message
实际包含的内容,即
- 为每个请求添加基本身份验证,其中凭据取决于某些
Message.headers
- 仅当特定
Message.headers
存在时才添加自定义 HTTP header
我考虑了以下解决方案:
- 正在实施自定义
ClientInterceptor
- 但无法访问
Message.header此时
- 实施自定义
SoapHeaderMapper
- 可以访问 Message.header
但我不喜欢这个想法,因为它本来就是
在 SOAP 信封上操作,而不是在 connection/request 级别上操作。
- 正在实施自定义
WebServiceMessageCallback
- 此时无法访问 Message。
此外,在身份验证的上下文中,上述所有解决方案都依赖于我们自己添加必要的 HTTP 身份验证 header,而我想以更正确的方式进行(至少在我看来)并且正确配置 HttpClient
.
所以在这一点上我完成了自定义 HttpComponentsMessageSender
,它为每个请求设置 HttpClientContext
。问题是再次无法访问 Mesage.headers
所以我完成了 ServiceActivator
与 SPEL 和 ThreadLocal
的某种组合,如下所示。
通常它是可行的,但是...还有其他更正确的路径吗?
<int:service-activator expression="@basicAuthenticationMessageSender.setBasicAuth(#root, headers.username, headers.pass)" />
public class BasicAuthenticationMessageSender extends HttpComponentsMessageSender {
private static ThreadLocal<HttpClientContext> httpClientContextLocal = new ThreadLocal<>();
@Override
protected HttpContext createContext(URI uri) {
HttpClientContext httpClientContext = httpClientContextLocal.get();
// This part makes authentication preemptive:
HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
AuthCache authCache = new BasicAuthCache();
authCache.put(targetHost, new BasicScheme());
httpClientContext.setAuthCache(authCache);
return httpClientContext;
}
public GenericMessage setBasicAuth(GenericMessage message, String username, String password) throws Exception {
final HttpClientContext httpClientContext = HttpClientContext.create();
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
httpClientContext.setCredentialsProvider(credentialsProvider);
httpClientContextLocal.set(httpClientContext);
return message;
}
}
Spring 集成 4.3.11
没错。由于 Spring 集成 WS 支持完全基于 Spring WS 项目,因此在 SI 中我们无法再使用 SWS 为我们提供的内容。
无论如何,您可以将 WebServiceMessageCallback
注入 AbstractWebServiceOutboundGateway
并在调用此 WS 网关之前访问 Message
填充的 ThreadLocal
变量。
在 WebServiceMessageCallback
中,您应该可以访问:
TransportContext context = TransportContextHolder.getTransportContext();
HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();
connection.getConnection().addRequestProperty("hea derParameter", "headerValue");
您可以从自定义 DefaultSoapHeaderMapper
访问相同的 TransportContext
,即使没有任何 ThreadLocal
:
@Override
protected void populateUserDefinedHeader(String headerName, Object headerValue, SoapMessage target) {
super.populateUserDefinedHeader(headerName, headerValue, target);
TransportContext context = TransportContextHolder.getTransportContext();
HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();
connection.getConnection().addRequestProperty(headerName, headerValue);
}
我想使用一些自定义 HTTP Headers 来丰富我的 HTTP/SOAP 请求(使用 WS 出站网关发送),具体取决于 Message
实际包含的内容,即
- 为每个请求添加基本身份验证,其中凭据取决于某些
Message.headers
- 仅当特定
Message.headers
存在时才添加自定义 HTTP header
我考虑了以下解决方案:
- 正在实施自定义
ClientInterceptor
- 但无法访问 Message.header此时 - 实施自定义
SoapHeaderMapper
- 可以访问Message.header
但我不喜欢这个想法,因为它本来就是 在 SOAP 信封上操作,而不是在 connection/request 级别上操作。 - 正在实施自定义
WebServiceMessageCallback
- 此时无法访问 Message。
此外,在身份验证的上下文中,上述所有解决方案都依赖于我们自己添加必要的 HTTP 身份验证 header,而我想以更正确的方式进行(至少在我看来)并且正确配置 HttpClient
.
所以在这一点上我完成了自定义 HttpComponentsMessageSender
,它为每个请求设置 HttpClientContext
。问题是再次无法访问 Mesage.headers
所以我完成了 ServiceActivator
与 SPEL 和 ThreadLocal
的某种组合,如下所示。
通常它是可行的,但是...还有其他更正确的路径吗?
<int:service-activator expression="@basicAuthenticationMessageSender.setBasicAuth(#root, headers.username, headers.pass)" />
public class BasicAuthenticationMessageSender extends HttpComponentsMessageSender {
private static ThreadLocal<HttpClientContext> httpClientContextLocal = new ThreadLocal<>();
@Override
protected HttpContext createContext(URI uri) {
HttpClientContext httpClientContext = httpClientContextLocal.get();
// This part makes authentication preemptive:
HttpHost targetHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
AuthCache authCache = new BasicAuthCache();
authCache.put(targetHost, new BasicScheme());
httpClientContext.setAuthCache(authCache);
return httpClientContext;
}
public GenericMessage setBasicAuth(GenericMessage message, String username, String password) throws Exception {
final HttpClientContext httpClientContext = HttpClientContext.create();
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
httpClientContext.setCredentialsProvider(credentialsProvider);
httpClientContextLocal.set(httpClientContext);
return message;
}
}
Spring 集成 4.3.11
没错。由于 Spring 集成 WS 支持完全基于 Spring WS 项目,因此在 SI 中我们无法再使用 SWS 为我们提供的内容。
无论如何,您可以将 WebServiceMessageCallback
注入 AbstractWebServiceOutboundGateway
并在调用此 WS 网关之前访问 Message
填充的 ThreadLocal
变量。
在 WebServiceMessageCallback
中,您应该可以访问:
TransportContext context = TransportContextHolder.getTransportContext();
HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();
connection.getConnection().addRequestProperty("hea derParameter", "headerValue");
您可以从自定义 DefaultSoapHeaderMapper
访问相同的 TransportContext
,即使没有任何 ThreadLocal
:
@Override
protected void populateUserDefinedHeader(String headerName, Object headerValue, SoapMessage target) {
super.populateUserDefinedHeader(headerName, headerValue, target);
TransportContext context = TransportContextHolder.getTransportContext();
HttpUrlConnection connection = (HttpUrlConnection) context.getConnection();
connection.getConnection().addRequestProperty(headerName, headerValue);
}