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;
}
}
Spring Boot (2.5.9) 在使用 spring-cloud-gcp-starter-secretmanager
ver 2.0.8 throwing error
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;
}
}