AwsSignerExecutionAttribute已弃用,如何用AWS Auth SDK签名WebFlux请求?
问题描述
现有一套对Spring WebFlux响应式ClientRequest请求进行签名的逻辑,原代码如下:
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) } }
但其中AwsSignerExecutionAttribute已被弃用,需要调整为AWS Auth SDK的当前推荐用法,同时保持对响应式ClientRequest的签名支持。
解决方案
AWS SDK v2中,Aws4Signer的签名逻辑已更新,不再依赖ExecutionAttributes传递签名参数,而是使用Aws4SignerParams封装必要的签名信息。以下是修改后的代码:
import org.springframework.util.MultiValueMapAdapter import org.springframework.web.reactive.function.client.ClientRequest import reactor.core.publisher.Mono import software.amazon.awssdk.auth.credentials.AwsCredentials import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider import software.amazon.awssdk.auth.signer.Aws4Signer import software.amazon.awssdk.auth.signer.Aws4SignerParams import software.amazon.awssdk.http.SdkHttpFullRequest import software.amazon.awssdk.http.SdkHttpMethod import software.amazon.awssdk.regions.Region import java.time.Instant 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> { // 构建SDK HTTP请求对象 val sdkRequest = SdkHttpFullRequest.builder() .method(SdkHttpMethod.fromValue(request.method().name())) .uri(request.url()) .apply { body?.let { contentStreamProvider { it.byteInputStream() } } } .headers(request.headers()) .build() // 获取AWS凭证 val credentials: AwsCredentials = awsCredentialsProvider.resolveCredentials() // 构建签名参数 val signerParams = Aws4SignerParams.builder() .credentials(credentials) .signingName(serviceName) .signingRegion(region) .signingClockOverride(Instant.now()) // 可选:指定签名时间,默认用系统时间 .build() // 执行签名 val signedSdkRequest = signer.sign(sdkRequest, signerParams) // 将签名后的头部映射回ClientRequest val signedClientRequest = ClientRequest.from(request) .headers { headers -> headers.clear() headers.addAll(MultiValueMapAdapter(signedSdkRequest.headers())) } .build() return Mono.just(signedClientRequest) } }
关键变化说明
- 替换弃用的
ExecutionAttributes:改用Aws4SignerParams封装签名所需的凭证、服务名、区域等参数,这是当前AWS SDK推荐的方式。 - 直接传递签名参数:不再通过属性键隐式传递信息,而是通过
Aws4SignerParams.Builder明确配置各项参数,代码可读性更强。 - 签名时间处理:可通过
signingClockOverride指定签名使用的时间,默认使用系统当前时间,通常无需额外配置。 - 保持响应式兼容:逻辑依然返回
Mono<ClientRequest>,完美适配Spring WebFlux的响应式调用链。
额外注意事项
- 确保使用的AWS SDK版本在
2.17.0及以上(Aws4SignerParams在此版本引入)。 - 如果请求体是动态生成的,需确保签名前能获取完整的请求体内容,因为AWS签名需要计算请求体哈希。
- 若需异步获取凭证,可将凭证解析逻辑改为响应式调用,适配异步场景:
override fun apply(request: ClientRequest): Mono<ClientRequest> { val sdkRequest = SdkHttpFullRequest.builder() .method(SdkHttpMethod.fromValue(request.method().name())) .uri(request.url()) .apply { body?.let { contentStreamProvider { it.byteInputStream() } } } .headers(request.headers()) .build() return Mono.fromCallable { awsCredentialsProvider.resolveCredentials() } .map { credentials -> val signerParams = Aws4SignerParams.builder() .credentials(credentials) .signingName(serviceName) .signingRegion(region) .build() signer.sign(sdkRequest, signerParams) } .map { signedSdkRequest -> ClientRequest.from(request) .headers { headers -> headers.clear() headers.addAll(MultiValueMapAdapter(signedSdkRequest.headers())) } .build() } }
内容的提问来源于stack exchange,提问作者pixel
相关产品推荐
相关产品推荐

