访问Amazon SQS时如何自动刷新过期的AWS STS临时安全凭证?
STS临时凭证访问SQS自动刷新解决方案
问题根因
你当前的配置会出现过期报错是两个原因导致的:
- 使用了
AWSStaticCredentialsProvider,这个实现类只会持有初始化时传入的固定凭证,不会主动更新 - 相关Bean都是默认单例,启动时初始化一次后就不会重新创建,凭证过期后没有机制更新
最优解决方案(推荐)
直接使用AWS SDK原生提供的STSAssumeRoleSessionCredentialsProvider,无需自己实现凭证刷新逻辑,SDK会自动在凭证快过期时调用STS接口获取新凭证。
修改后的配置代码如下,可直接替换你现有的两套配置:
@Configuration @Slf4j public class QueueConfig { @Value("${aws.sts.role-arn:XXXX}") private String roleArn; @Value("${aws.sts.role-session-name:XXX}") private String roleSessionName; @Value("${aws.region:us-west-1}") private String region; @Bean public QueueMessagingTemplate queueMessagingTemplate(AmazonSQSAsync amazonSQSAsync) { log.info("queueMessagingTemplate init"); return new QueueMessagingTemplate(amazonSQSAsync); } @Bean @Primary public AmazonSQSAsync amazonSQSAsync() { // 初始化支持自动刷新的STS凭证提供器 STSAssumeRoleSessionCredentialsProvider credentialsProvider = new STSAssumeRoleSessionCredentialsProvider.Builder(roleArn, roleSessionName) .withStsClient(AmazonSTSClientBuilder.standard().withRegion(region).build()) // 可选配置:会话有效期,最大可设12小时,需要和角色的最大会话时长配置匹配 .withSessionDurationSeconds(3600) .build(); return AmazonSQSAsyncClientBuilder .standard() .withRegion(region) // 替换原来的静态凭证提供器 .withCredentials(credentialsProvider) .build(); } }
如果你使用的是AWS SDK V2,对应自动刷新凭证的类为
StsAssumeRoleCredentialsProvider,使用逻辑完全一致。
自定义刷新方案(特殊场景用)
如果你有自定义的STS调用逻辑,不能直接用SDK自带的提供器,可以自行实现凭证刷新逻辑,示例如下:
- 实现自动刷新的凭证提供器
@Component @Slf4j public class AutoRefreshStsCredentialsProvider implements AWSCredentialsProvider { private BasicSessionCredentials currentCredentials; private Instant expirationTime; // 提前5分钟刷新凭证,避免过期 private static final Duration REFRESH_AHEAD_TIME = Duration.ofMinutes(5); private static final String ROLE_ARN = "XXXX"; private static final String ROLE_SESSION_NAME = "XXX"; private static final Region REGION = Region.US_WEST_1; @Override public AWSCredentials getCredentials() { if (currentCredentials == null || Instant.now().isAfter(expirationTime.minus(REFRESH_AHEAD_TIME))) { refreshCredentials(); } return currentCredentials; } private synchronized void refreshCredentials() { // 双重检查避免多线程重复刷新 if (currentCredentials != null && !Instant.now().isAfter(expirationTime.minus(REFRESH_AHEAD_TIME))) { return; } try (StsClient stsClient = StsClient.builder().region(REGION).build()) { AssumeRoleRequest roleRequest = AssumeRoleRequest.builder() .roleArn(ROLE_ARN) .roleSessionName(ROLE_SESSION_NAME) .build(); AssumeRoleResponse roleResponse = stsClient.assumeRole(roleRequest); Credentials newCreds = roleResponse.credentials(); this.currentCredentials = new BasicSessionCredentials( newCreds.accessKeyId(), newCreds.secretAccessKey(), newCreds.sessionToken() ); this.expirationTime = newCreds.expiration(); } catch (StsException e) { log.error("STS凭证刷新失败", e); // 刷新失败时如果旧凭证还未过期,继续使用旧凭证,避免业务直接报错 if (currentCredentials == null || Instant.now().isAfter(expirationTime)) { throw new RuntimeException("无有效可用的STS凭证", e); } } } @Override public void refresh() { refreshCredentials(); } }
- 将自定义提供器注入SQS客户端
@Bean @Primary public AmazonSQSAsync amazonSQSAsync(AutoRefreshStsCredentialsProvider credentialsProvider) { return AmazonSQSAsyncClientBuilder .standard() .withRegion(Regions.US_WEST_1) .withCredentials(credentialsProvider) .build(); }
注意事项
- 不要使用
AWSStaticCredentialsProvider承载临时STS凭证,这个类仅适合长期不变的AKSK场景 - 若项目中多个AWS服务共用同一个STS角色,可以把凭证提供器抽为公共Bean复用,避免重复调用STS接口
- 生产环境建议将角色ARN、区域、会话名等配置放到配置文件中,不要硬编码在代码里
内容的提问来源于stack exchange,提问作者suru
相关产品推荐
相关产品推荐

