You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 21:05:19