最近公司为了访问安全,给Zookeeper 设置了用户名密码,这样SpringCloud用Zookeeper 作为注册中心的服务就访问不了,网上搜了很多也没讲如何配置的,都是讲Zookeeper 如何设置密码的。按惯例是解决了,不然也不会心血来潮写个文章来记录下~!

一、SpringCloud注册中心配置

1、引用jar包

<dependency>
   <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
 </dependency>

2、在启动项配置开启注解

@SpringBootApplication
@EnableDiscoveryClient
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

3、在application.yml配置参数

# 应用服务端口
server.port: 8080
# 应用名称
spring.application.name: demo-center
# spring-cloud
spring.cloud:
  zookeeper.connect-string: 127.0.0.1:2181
  loadbalancer:
    ribbon.enabled: false
    cache.enabled: false

二、源码分析

这样基于Zookeeper为注册中心就配置好了,如果Zookeeper需要用户密码访问再怎么配置,Spring Cloud Zookeeper官网文档介绍:在Zookeeper自动配置类初始化的时候通过CuratorFramework可以设置认证的用户名和密码,请参阅 Zookeeper 自动配置类

Zookeeper 在自动配置的时候生成一个CuratorFramework ,查看CuratorFramework 源码并没有提供addAuthInfo方法(官方文档也有出错的时候~)

	@Bean(destroyMethod = "close")
	@ConditionalOnMissingBean
	public CuratorFramework curatorFramework(RetryPolicy retryPolicy, ZookeeperProperties properties,
			ObjectProvider<CuratorFrameworkCustomizer> optionalCuratorFrameworkCustomizerProvider,
			ObjectProvider<EnsembleProvider> optionalEnsembleProvider,
			ObjectProvider<TracerDriver> optionalTracerDriverProvider) throws Exception {
			
		CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
		EnsembleProvider ensembleProvider = optionalEnsembleProvider.getIfAvailable();
		if (ensembleProvider != null) {
			builder.ensembleProvider(ensembleProvider);
		} else {
			builder.connectString(properties.getConnectString());
		}
		builder.sessionTimeoutMs((int) properties.getSessionTimeout().toMillis())
				.connectionTimeoutMs((int) properties.getConnectionTimeout().toMillis())
				.retryPolicy(retryPolicy);

		optionalCuratorFrameworkCustomizerProvider.orderedStream()
			.forEach(curatorFrameworkCustomizer -> curatorFrameworkCustomizer.customize(builder));

		CuratorFramework curator = builder.build();
		optionalTracerDriverProvider.ifAvailable(tracerDriver -> {
			if (curator.getZookeeperClient() != null) {
				curator.getZookeeperClient().setTracerDriver(tracerDriver);
			}
		});
		curator.start();
		if (log.isTraceEnabled()) {
			log.trace("blocking until connected to zookeeper for "+ properties.getBlockUntilConnectedWait() + properties.getBlockUntilConnectedUnit());
		}
		curator.blockUntilConnected(properties.getBlockUntilConnectedWait(), properties.getBlockUntilConnectedUnit());
		if (log.isTraceEnabled()) {
			log.trace("connected to zookeeper");
		}
		return curator;
	}

但是通过自动配置代码可以看到CuratorFramework 是通过CuratorFrameworkFactory.Builder生成的,这种是spring常用代码写作技巧,查看源码Builder提供一个authorization和文档上说的一样可以添加用户名和密码。

CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
builder.authorization("digest", "username :password").getBytes());

可以看到Builder 是方法内部变量,如何在方法外调用到Builder ,或者是直接重写Zookeeper 自动配置再加入authorization;看源码在builder.build()之前,builder对象还被optionalCuratorFrameworkCustomizerProvider调用

	optionalCuratorFrameworkCustomizerProvider.orderedStream()
			.forEach(curatorFrameworkCustomizer -> 
				curatorFrameworkCustomizer.customize(builder));

optionalCuratorFrameworkCustomizerProvider是通过Sring容器注入进来的

public CuratorFramework curatorFramework(
			RetryPolicy retryPolicy, 
			ZookeeperProperties properties,
			ObjectProvider<CuratorFrameworkCustomizer> optionalCuratorFrameworkCustomizerProvider,
			ObjectProvider<EnsembleProvider> optionalEnsembleProvider,
			ObjectProvider<TracerDriver> optionalTracerDriverProvider) throws Exception {

由此可见ObjectProvider<CuratorFrameworkCustomizer> 是框架专门开放出来给用户添加个性化属性准备的

@FunctionalInterface
public interface CuratorFrameworkCustomizer {
	void customize(CuratorFrameworkFactory.Builder builder);
}

三、配置ZK用户名密码

通过上面的源码解析,我们只要实现CuratorFrameworkCustomizer 注入到Spring容器中,Zookeeper 在自动配置的时候就可以调用到了,上代码:

@Configuration
public class ZookeeperConfigurer implements CuratorFrameworkCustomizer {
    @Value("${spring.cloud.zookeeper.username:}")
    private String username;
    @Value("${spring.cloud.zookeeper.password:}")
    private String password;
    @Override
    public void customize(CuratorFrameworkFactory.Builder builder) {
        if (StrUtil.isNotBlank(username) && StrUtil.isNotBlank(password)) {
            builder.authorization("digest", (username + ":" + password).getBytes());
        }
    }
}

这里调用配置文件,如果配置文件配置了用户名密码就配置,如果没有配置就略过,配置文件可以改为

# spring-cloud
spring.cloud:
  zookeeper:
    connect-string: 127.0.0.1:2181
    username: username
    password: password
  loadbalancer:
    ribbon.enabled: false
    cache.enabled: false

由此就顺利解决SpringCloud Zookeeper 配置用户名密码 的问题了。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐