Mar*_*nyi 8 java spring reactive-programming amazon-web-services spring-webflux
我想AWS对由 Spring 的响应式WebClient触发的 HTTP 请求进行签名。要签署请求,我需要访问以下内容:URL、HTTP 方法、查询参数、标头和请求正文字节。
我从编写ExchangeFilterFunction开始。由于ClientRequest接口,我可以访问我需要的所有内容,除了请求正文:
@Component
public class AwsSigningInterceptor implements ExchangeFilterFunction
{
private final AwsHeaderSigner awsHeaderSigner;
public AwsSigningInterceptor(AwsHeaderSigner awsHeaderSigner)
{
this.awsHeaderSigner = awsHeaderSigner;
}
@Override
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next)
{
Map<String, List<String>> signingHeaders = awsHeaderSigner.createSigningHeaders(request, new byte[]{}, "es", "us-west-2"); // should pass request body bytes in place of new byte[]{}
ClientRequest.Builder requestBuilder = ClientRequest.from(request);
signingHeaders.forEach((key, value) -> requestBuilder.header(key, value.toArray(new String[0])));
return next.exchange(requestBuilder.build());
}
}
Run Code Online (Sandbox Code Playgroud)
在旧的 Spring 版本中,我们将RestTemplate与ClientHttpRequestInterceptor一起使用。在这种情况下,主体的字节被暴露,因此可以进行签名。
正如我所看到的,在WebClient Spring 将主体作为Publisher处理时,所以我不确定ExchangeFilterFunction是否是一个好的起点。
我应该如何签署 HTTP 请求?
我提出了以下解决方案:
import org.springframework.util.MultiValueMapAdapter
import org.springframework.web.reactive.function.client.ClientRequest
import reactor.core.publisher.Mono
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider
import software.amazon.awssdk.auth.signer.Aws4Signer
import software.amazon.awssdk.auth.signer.AwsSignerExecutionAttribute
import software.amazon.awssdk.core.interceptor.ExecutionAttributes
import software.amazon.awssdk.http.SdkHttpFullRequest
import software.amazon.awssdk.http.SdkHttpMethod
import software.amazon.awssdk.regions.Region
import java.util.function.Function
class WebClientAwsSigner(
private val body: String? = null,
private val signer: Aws4Signer,
private val awsCredentialsProvider: AwsCredentialsProvider,
private val serviceName: String,
private val region: Region,
) : Function<ClientRequest, Mono<ClientRequest>> {
override fun apply(request: ClientRequest): Mono<ClientRequest> {
val requestBuilder = SdkHttpFullRequest.builder()
.method(SdkHttpMethod.fromValue(request.method().name()))
.uri(request.url())
.apply {
if (body != null) {
contentStreamProvider { body.byteInputStream() }
}
}
.headers(request.headers())
val attributes = ExecutionAttributes()
attributes.putAttribute(
AwsSignerExecutionAttribute.AWS_CREDENTIALS,
awsCredentialsProvider.resolveCredentials(),
)
attributes.putAttribute(AwsSignerExecutionAttribute.SERVICE_SIGNING_NAME, serviceName)
attributes.putAttribute(AwsSignerExecutionAttribute.SIGNING_REGION, region)
val signedAwsRequest = signer.sign(requestBuilder.build(), attributes)
val signedClientRequest =
ClientRequest.from(request)
.headers {
it.clear()
it.addAll(MultiValueMapAdapter(signedAwsRequest.headers()))
}
.build()
return Mono.just(signedClientRequest)
}
}
private fun signedAwsWebClient(body: String? = null): WebClient = webclient
.mutate().filter(
ExchangeFilterFunction.ofRequestProcessor(
WebClientAwsSigner(body, Aws4Signer.create(), DefaultCredentialsProvider.create(), "es", Region.US_EAST_1),
),
)
.build()
Run Code Online (Sandbox Code Playgroud)
GitHub: https: //github.com/kkocel/webclient-signed-request-to-aws
| 归档时间: |
|
| 查看次数: |
1373 次 |
| 最近记录: |