S3预签名URL部署一段时间后持续提示令牌过期问题排查
解决Spring Boot 3中S3预签名URL生成时“提供的令牌已过期”问题
问题背景
服务基于Spring Boot 3开发,使用EKS服务账户的Web Identity Token获取STS凭证生成S3预签名URL。刚部署时功能正常,但运行一段时间后持续抛出“提供的令牌已过期”错误。
原生成代码:
private URL generatePreSignedUrl(String s3Key, String s3Bucket, Date expiration, HttpMethod method) { GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(s3Bucket, s3Key) .withMethod(method) .withExpiration(expiration); return amazonS3.generatePresignedUrl(generatePresignedUrlRequest); }
原配置:
spring: cloud: aws: credentials: sts: web-identity-token-file: /var/run/secrets/eks.amazonaws.com/serviceaccount/token cloud: aws: region: static: us-east-1 stack: auto: false credentials: use-default-aws-credentials-chain: true
问题根源
- 配置冲突:
spring.cloud.aws和cloud.aws混合使用,Spring Cloud AWS 3.x(适配Spring Boot 3)仅识别spring.cloud.aws前缀的配置,导致部分凭证配置未生效。 - 旧SDK客户端限制:使用的
AmazonS3是AWS SDK v1的客户端,它不会自动刷新过期的STS临时凭证。EKS服务账户令牌对应的STS凭证有默认1小时的有效期,过期后客户端无法自动获取新凭证,从而报错。
解决方案
1. 统一并修正配置
删除cloud.aws前缀的配置,统一使用spring.cloud.aws,确保STS凭证自动刷新的配置生效:
spring: cloud: aws: region: static: us-east-1 stack: auto: false credentials: sts: web-identity-token-file: /var/run/secrets/eks.amazonaws.com/serviceaccount/token role-arn: # 替换为你的EKS服务账户关联的IAM角色ARN,必须配置 s3: endpoint: # 可选,若使用S3兼容存储或特定区域端点可配置
注意:必须配置
role-arn,否则STS无法正确获取临时凭证。
2. 切换到AWS SDK v2的S3客户端
Spring Cloud AWS 3.x推荐使用AWS SDK v2的客户端,它会自动处理临时凭证的刷新。
Maven依赖示例:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-aws-s3</artifactId> <version>3.2.1</version> <!-- 适配Spring Boot 3.x的最新版本 --> </dependency>
3. 修改预签名URL生成代码
使用AWS SDK v2的S3Presigner来生成预签名URL,它会自动使用刷新后的凭证:
import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.presigner.S3Presigner; import software.amazon.awssdk.services.s3.presigner.model.GetObjectPresignRequest; import software.amazon.awssdk.services.s3.presigner.model.PutObjectPresignRequest; import java.net.URL; import java.time.Duration; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Service; @Service public class S3Service { private final S3Presigner s3Presigner; // 构造函数注入S3Presigner,Spring Cloud AWS会自动配置 public S3Service(S3Presigner s3Presigner) { this.s3Presigner = s3Presigner; } public URL generatePreSignedUrl(String s3Key, String s3Bucket, Duration expiration, HttpMethod method) { if (HttpMethod.GET.equals(method)) { GetObjectRequest getObjectRequest = GetObjectRequest.builder() .bucket(s3Bucket) .key(s3Key) .build(); GetObjectPresignRequest presignRequest = GetObjectPresignRequest.builder() .signatureDuration(expiration) .getObjectRequest(getObjectRequest) .build(); return s3Presigner.presignGetObject(presignRequest).url(); } else if (HttpMethod.PUT.equals(method)) { PutObjectRequest putObjectRequest = PutObjectRequest.builder() .bucket(s3Bucket) .key(s3Key) .build(); PutObjectPresignRequest presignRequest = PutObjectPresignRequest.builder() .signatureDuration(expiration) .putObjectRequest(putObjectRequest) .build(); return s3Presigner.presignPutObject(presignRequest).url(); } // 可扩展其他HTTP方法 throw new IllegalArgumentException("Unsupported HTTP method: " + method); } }
4. 验证凭证刷新机制
AWS SDK v2的S3Presigner会自动使用Spring Cloud AWS配置的凭证提供者,该提供者会定期检查STS凭证的有效期,在过期前自动获取新的临时凭证,无需手动干预。
额外注意事项
- 确保EKS服务账户已正确关联IAM角色,且角色拥有生成S3预签名URL所需的权限(如
s3:GetObject、s3:PutObject等)。 - 若需要自定义STS凭证有效期,可在配置中添加
spring.cloud.aws.credentials.sts.session-duration设置(单位:秒,默认3600秒)。
内容的提问来源于stack exchange,提问作者Ahmad M AbuGhozeh
相关产品推荐
相关产品推荐

