GCP Secret Manager 和 Spring 引导应用程序出现问题

Problem with GCP Secret Manager and Spring Boot app

Spring Boot (2.5.9) 在使用 spring-cloud-gcp-starter-secretmanager ver 2.0.8 throwing error

从 GCP Secret Manager 访问密码时遇到问题
AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultFeignClientConfiguration': Injection of autowired dependencies failed; nested exception is org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [com.google.protobuf.ByteString$LiteralByteString] to type [java.lang.String]

对于在 application.properties 中声明的密码为

webservices.security.basic.user.password=${sm://my-password}

当我将其替换为常规字符串甚至 env 变量时,它会正常工作。

代码的失败部分如下所示:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;

import feign.Retryer;
import feign.auth.BasicAuthRequestInterceptor;
import feign.codec.ErrorDecoder;

/**
 * Default feign client configuration.  Includes retry policies, basic auth user name and password, and HTTP status decoder.
 * @author Greg Meyer
 * @since 6.0
 */
public class DefaultFeignClientConfiguration
{   
    @Value("${webservices.retry.backoff.multiplier:3}")
    protected double backoffMultiplier;
    
    @Value("${webservices.retry.backoff.initialBackoffInterval:100}")
    protected long initialBackoffInterval;  
    
    @Value("${webservices.retry.backoff.maxInterval:20000}")
    protected long maxInterval;     
    
    @Value("${webservices.security.basic.user.name:}")
    protected String user;  
    
    @Value("${webservices.security.basic.user.password:}")
    protected String pass;  
    
    /**
     * Creates an instance of the a the default HTTP status translator.
     * @return An instance of the a the default HTTP status translator
     */
    @Bean
    public ErrorDecoder feignClientErrorDecoder()
    {
        return new DefaultErrorDecoder();
    }
    
    /**
     * Creates an instance of BasicAuth interceptor configured with a username and password.   This bean is only created if the
     * "webservices.security.basic.user.name" property is set.
     * @return An instance of BasicAuth interceptor configured with a username and password
     */
    @Bean
    @ConditionalOnProperty(name="webservices.security.basic.user.name", matchIfMissing=false)
    public BasicAuthRequestInterceptor basicAuthRequestInterceptor() 
    {
        return new BasicAuthRequestInterceptor(user, pass);
    }   
    
    /**
     * Creates an instance of a back off policy used in conjuntion with the retry policy.
     * @return An instance of a back off policy
     */
    @Bean
    public LoadBalancedRetryFactory backOffPolciyFactory()
    {
        return new LoadBalancedRetryFactory() 
        {
            @Override
            public BackOffPolicy createBackOffPolicy(String service) 
            {
                final ExponentialBackOffPolicy backoffPolicy = new ExponentialBackOffPolicy();
                backoffPolicy.setMultiplier(backoffMultiplier);
                backoffPolicy.setInitialInterval(initialBackoffInterval);
                backoffPolicy.setMaxInterval(maxInterval);
                
                return backoffPolicy;
            }
        };      
    }
    
    /**
     * Creates a default http retry policy.
     * @return A default http retry policy.
     */ 
    @Bean
    public Retryer retryer() 
    {
        /*
         * Default retryer config
         */
        return new Retryer.Default(200, 1000, 5);
    }
}

有什么想法吗?

问题是,Feign 自动配置很可能发生在早期,在 GcpSecretManagerEnvironmentPostProcessor 有机会 运行 并引入 ByteString 转换器之前。

基本上,有效的解决方案是实现一个实现通用转换服务的自定义转换器,并在调用 SpringApplication.run 之前在 main 方法中注册转换器。

    public static void main(String[] args) 
    {
        ((DefaultConversionService)DefaultConversionService.getSharedInstance()).addConverter(new CustomConverter());
        SpringApplication.run(STAApplication.class, args);
    }  

和自定义转换器:

@Component
public class CustomConverter implements GenericConverter {
    @Override
    public Set<ConvertiblePair> getConvertibleTypes() {
        return Collections.singleton(new ConvertiblePair(ByteString.class, String.class));
    }

    @Override
    public Object convert(Object source, TypeDescriptor sourceType,
                          TypeDescriptor targetType) {
        if (sourceType.getType() == String.class) {
            return source;
        }
        try {
            source = ((ByteString) source).toString("UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return source;
    }
}