Spring Vault库令牌续期失败时Authentication Error Listener未执行
问题:Spring Vault令牌续期失败时AuthenticationErrorListener未触发
我在Spring Vault库中配置了实现AuthenticationErrorListener的VaultEventListener,并在sessionManager()方法中将其添加到会话。需求是现有令牌续期调用出错时触发该监听器进行多次重试,但目前令牌续期失败时监听器并未执行。
相关代码
SntlVaultGcpIamAuthConfig类
class SntlVaultGcpIamAuthConfig extends AbstractVaultConfiguration { static final Logger logger = LogManager.getLogger(SntlVaultGcpIamAuthConfig.class); @Override public ClientAuthentication clientAuthentication() { try { GoogleCredentials credential = GoogleCredentials.getApplicationDefault(); String account = null; if (credential instanceof ServiceAccountCredentials) { account = ((ServiceAccountCredentials) credential).getAccount(); } else if (credential instanceof ComputeEngineCredentials) { account = ((ComputeEngineCredentials) credential).getAccount(); } else { logger.fatal( "Unable to get service account email, Application default GoogleCredentials must be instance of ServiceAccountCredentials or ComputeEngineCredentials"); return null; } logger.info("Application running with service account : {}", account); GcpIamCredentialsAuthenticationOptions options = GcpIamCredentialsAuthenticationOptions.builder() .role(SntlVaultConfig.getGcpVaultLoginRole()).credentials(credential).serviceAccountId(account) .build(); GcpIamCredentialsAuthentication authN = new GcpIamCredentialsAuthentication(options, this.restOperations()); return authN; } catch (IOException | SntlCloudEnvException e) { logger.fatal( "An exception occurred in setting up GCP IAM authentication for Vault.", e);; } return null; } @Override public VaultEndpoint vaultEndpoint() { VaultEndpoint endPoint = new VaultEndpoint(); try { endPoint.setHost(SntlVaultConfig.getVaultAddr()); endPoint.setPort(SntlVaultConfig.getVaultPort()); endPoint.setScheme(SntlVaultConfig.getVaultEndPointScheme()); return endPoint; } catch (SntlCloudEnvException e) { logger.fatal( "SntlCloudEnvException occurred .", e); return null; } } @Override public ClientOptions clientOptions() { try { return new ClientOptions(Duration.ofSeconds(SntlVaultConfig.getVaultConnectionTimeout()), Duration.ofSeconds(SntlVaultConfig.getVaultReadTimeout())); } catch (SntlCloudEnvException e) { logger.fatal( "SntlCloudEnvException occurred .", e); return null; } } @Override @Bean @Primary public SessionManager sessionManager() { boolean retryEnabled = Boolean.parseBoolean(SntlVaultConfig.isRetryTokenRenewal); int REFRESH_PERIOD_BEFORE_EXPIRY = Integer.parseInt(SntlVaultConfig.refreshBeforeExpiryDuration); //120; // Overrides default which is 5 seconds ClientAuthentication clientAuthentication = clientAuthentication(); Assert.notNull(clientAuthentication, "ClientAuthentication must not be null"); LifecycleAwareSessionManagerSupport.FixedTimeoutRefreshTrigger refreshTrigger = new LifecycleAwareSessionManagerSupport.FixedTimeoutRefreshTrigger( REFRESH_PERIOD_BEFORE_EXPIRY, TimeUnit.SECONDS); LifecycleAwareSessionManager lifecycleAwareSessionManager = new LifecycleAwareSessionManager(clientAuthentication, getVaultThreadPoolTaskScheduler(), restOperations(), refreshTrigger); lifecycleAwareSessionManager.setLeaseStrategy(LeaseStrategy.retainOnError()); //To-validate VaultEventListener vaultEventListener = new VaultEventListener(); lifecycleAwareSessionManager.addErrorListener(vaultEventListener); return lifecycleAwareSessionManager; } }
VaultEventListener类
public class VaultEventListener implements AuthenticationErrorListener { private static final Logger logger = LoggerFactory.getLogger(VaultEventListener.class); @Override public void onAuthenticationError(AuthenticationErrorEvent event) { logger.info("AuthenticationErrorListener Error Source: {}", event.getSource()); logger.info("AuthenticationErrorListener Exception: {}", event.getException()); if (Boolean.parseBoolean(SntlVaultConfig.isRetryTokenRenewal)) { AnnotationConfigApplicationContext context = SntlCloudEnvContext.getContext(); SntlVaultGcpIamAuthConfig sntlVaultGcpIamAuthConfig = context.getBean(SntlVaultGcpIamAuthConfig.class); sntlVaultGcpIamAuthConfig.refreshToken(); } } }
问题排查与解决思路
1. 事件类型不匹配
AuthenticationErrorListener仅监听认证阶段的错误,而令牌续期属于租约管理操作,触发的是LeaseErrorEvent而非AuthenticationErrorEvent。需要同时实现LeaseErrorListener来捕获续期错误:
public class VaultEventListener implements AuthenticationErrorListener, LeaseErrorListener { private static final Logger logger = LoggerFactory.getLogger(VaultEventListener.class); @Override public void onAuthenticationError(AuthenticationErrorEvent event) { // 原有认证错误处理逻辑 logger.info("AuthenticationErrorListener Error Source: {}", event.getSource()); logger.info("AuthenticationErrorListener Exception: {}", event.getException()); } @Override public void onLeaseError(LeaseErrorEvent event) { logger.info("LeaseErrorListener Error Source: {}", event.getSource()); logger.info("LeaseErrorListener Exception: {}", event.getException()); if (Boolean.parseBoolean(SntlVaultConfig.isRetryTokenRenewal)) { AnnotationConfigApplicationContext context = SntlCloudEnvContext.getContext(); SntlVaultGcpIamAuthConfig sntlVaultGcpIamAuthConfig = context.getBean(SntlVaultGcpIamAuthConfig.class); sntlVaultGcpIamAuthConfig.refreshToken(); } } }
同时在sessionManager()中添加租约错误监听器:
lifecycleAwareSessionManager.addLeaseErrorListener(vaultEventListener);
2. LeaseStrategy配置影响事件发布
你设置了LeaseStrategy.retainOnError(),该策略会在续期失败时保留现有租约,但可能抑制错误事件的发布。可以尝试改为默认策略:
lifecycleAwareSessionManager.setLeaseStrategy(LeaseStrategy.defaultStrategy());
或者确认该策略下的事件触发逻辑是否符合预期。
3. 重试逻辑优化
直接通过上下文获取bean调用refreshToken()存在线程安全风险,建议注入SessionManager直接调用官方的刷新方法:
public class VaultEventListener implements AuthenticationErrorListener, LeaseErrorListener { private static final Logger logger = LoggerFactory.getLogger(VaultEventListener.class); private final SessionManager sessionManager; public VaultEventListener(SessionManager sessionManager) { this.sessionManager = sessionManager; } @Override public void onLeaseError(LeaseErrorEvent event) { logger.info("LeaseErrorListener Error Source: {}", event.getSource()); logger.info("LeaseErrorListener Exception: {}", event.getException()); if (Boolean.parseBoolean(SntlVaultConfig.isRetryTokenRenewal)) { try { sessionManager.refresh(); } catch (VaultException e) { logger.error("重试令牌刷新失败", e); } } } // 原有onAuthenticationError方法... }
然后在sessionManager()中初始化监听器时传入实例:
VaultEventListener vaultEventListener = new VaultEventListener(lifecycleAwareSessionManager);
内容的提问来源于stack exchange,提问作者Raju Yadav
相关产品推荐
相关产品推荐

