【分布式】ZooKeeper权限控制之ACL(Access Control List)访问控制列表
zk做为分布式架构中的重要中间件,通常会在上面以节点的方式存储一些关键信息,默认情况下,所有应用都可以读写任何节点,在复杂的应用中,这不太安全,ZK通过ACL机制来解决访问权限问题,详见官网文档:http://zookeeper.apache.org/doc/r3.4.6/zookeeperProgrammers.html#sc_ZooKeeperAccessControl
总体来说,ZK的节点有5种操作权限:
CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、删、改、查、管理权限,这5种权限简写为crwda(即:每个单词的首字符缩写)
注:这5种权限中,delete是指对子节点的删除权限,其它4种权限指对自身节点的操作权限
身份的认证有4种方式:
world:默认方式,相当于全世界都能访问
auth:代表已经认证通过的用户(cli中可以通过addauth digest user:pwd 来添加当前上下文中的授权用户)
digest:即用户名:密码这种方式认证,这也是业务系统中最常用的
ip:使用Ip地址认证
Cli命令行下可以这样测试:

通过getAcl命令可以发现,刚创建的节点,默认是 world,anyone的认证方式,具有cdrwa所有权限
继续捣鼓:

先给/test增加了user1:+owfoSBn/am19roBPzR1/MfCblE的只读(r)权限控制,
说明:setAcl /test digest:用户名:密码:权限 给节点设置ACL访问权限时,密码必须是加密后的内容,这里的+owfoSBn/am19roBPzR1/MfCblE=,对应的原文是12345 (至于这个密文怎么得来的,后面会讲到,这里先不管这个),设置完Acl后,可以通过
getAcl /节点路径 查看Acl设置
然后get /test时,提示认证无效,说明访问控制起作用了,接下来:
addauth digest user1:12345 给"上下文"增加了一个认证用户,即对应刚才setAcl的设置
然后再 get /test 就能取到数据了
最后 delete /test 成功了!原因是:根节点/默认是world:anyone:crdwa(即:全世界都能随便折腾),所以也就是说任何人,都能对根节点/进行读、写、创建子节点、管理acl、以及删除子节点(再次映证了ACL中的delete权限应该理解为对子节点的delete权限)
刚才也提到了,setAcl /path digest这种方式,必须输入密码加密后的值,这在cli控制台上很不方便,所以下面这种方式更常用:

注意加框的部分,先用addauth digest user1:12345 增加一个认证用户,然后用 setAcl /test auth:user1:12345:r 设置权限,跟刚才的效果一样,但是密码这里输入的是明文,控制台模式下手动输入更方便。
好了,揭开加密规则:
|
1
2
3
4
5
6
7
|
static public String generateDigest(String idPassword) throws NoSuchAlgorithmException { String parts[] = idPassword.split(":", 2); byte digest[] = MessageDigest.getInstance("SHA1").digest( idPassword.getBytes()); return parts[0] + ":" + base64Encode(digest);} |
就是SHA1加密,然后base64编码
代码使用:
zookeeper有一个很好用的客户端开源项目zkclient,官网地址为:http://github.com/zkclient ,其最新片0.7-dev已经支持ACL了(旧0.1版无此功能,所以推荐使用最新版),使用方法:
git clone https://github.com/sgroschupf/zkclient (把代码拉到本地)
修改
build.gradle 找到92行
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
uploadArchives { repositories.mavenDeployer { //repository(url: "file:///tmp/mavenRepo") repository(url: "http://172.21.129.56:8081/nexus/content/repositories/thirdparty/") { authentication(userName: admin, password: admin123) } beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } pom.project { name 'ZkClient' packaging 'jar' description 'A zookeeper client, that makes life a little easier.' url 'https://github.com/sgroschupf/zkclient' licenses { license { name 'The Apache Software License, Version 2.0' url 'http://www.apache.org/licenses/LICENSE-2.0.txt' distribution 'repo' } } scm { url 'https://github.com/sgroschupf/zkclient' connection 'scm:git:git://github.com/sgroschupf/zkclient.git' developerConnection 'scm:git:https://github.com/sgroschupf/zkclient.git' } developers { developer { id 'sgroschupf' name 'Stefan Groshupf' } developer { id 'pvoss' name 'Peter Voss' } developer { id 'jzillmann' name 'Johannes Zillmann' } } } }} |
把这一段干掉,否则编译时会出错
然后(windows环境,把./gradew 换成gradlew)
./gradlew test (测试)
./gradlew jars (编译生成jar包)
./gradlew install (安装到本机maven仓库)
新建一个maven项目,pom.xml参考下面设置:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xmlns="http://maven.apache.org/POM/4.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5 <modelVersion>4.0.0</modelVersion>
6
7 <groupId>yjmyzz</groupId>
8 <artifactId>zkclient-demo</artifactId>
9 <version>1.0</version>
10
11 <dependencies>
12 <dependency>
13 <groupId>org.apache.zookeeper</groupId>
14 <artifactId>zookeeper</artifactId>
15 <version>3.4.6</version>
16 </dependency>
17
18 <dependency>
19 <groupId>com.101te</groupId>
20 <artifactId>zkclient</artifactId>
21 <version>0.7</version>
22 <classifier>dev</classifier>
23 </dependency>
24
25 <dependency>
26 <groupId>log4j</groupId>
27 <artifactId>log4j</artifactId>
28 <version>1.2.17</version>
29 </dependency>
30
31 </dependencies>
32
33
34 </project>

然后写一段代码测试一下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
|
package yjmyzz.zk;import org.I0Itec.zkclient.ZkClient;import org.apache.zookeeper.*;import org.apache.zookeeper.data.ACL;import org.apache.zookeeper.data.Id;import org.apache.zookeeper.data.Stat;import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;import java.io.IOException;import java.security.NoSuchAlgorithmException;import java.util.ArrayList;import java.util.List;import java.util.Map;public class Main { private static final String zkAddress = "localhost:2181"; private static final String testNode = "/test"; private static final String readAuth = "read-user:123456"; private static final String writeAuth = "write-user:123456"; private static final String deleteAuth = "delete-user:123456"; private static final String allAuth = "super-user:123456"; private static final String adminAuth = "admin-user:123456"; private static final String digest = "digest"; private static void initNode() throws NoSuchAlgorithmException { ZkClient zkClient = new ZkClient(zkAddress); zkClient.addAuthInfo(digest, allAuth.getBytes()); if (zkClient.exists(testNode)) { zkClient.delete(testNode); System.out.println("节点删除成功!"); } List<ACL> acls = new ArrayList<ACL>(); acls.add(new ACL(ZooDefs.Perms.ALL, new Id(digest, DigestAuthenticationProvider.generateDigest(allAuth)))); acls.add(new ACL(ZooDefs.Perms.READ, new Id(digest, DigestAuthenticationProvider.generateDigest(readAuth)))); acls.add(new ACL(ZooDefs.Perms.WRITE, new Id(digest, DigestAuthenticationProvider.generateDigest(writeAuth)))); acls.add(new ACL(ZooDefs.Perms.DELETE, new Id(digest, DigestAuthenticationProvider.generateDigest(deleteAuth)))); acls.add(new ACL(ZooDefs.Perms.ADMIN, new Id(digest, DigestAuthenticationProvider.generateDigest(adminAuth)))); zkClient.createPersistent(testNode, "test-data", acls); System.out.println(zkClient.readData(testNode)); System.out.println("节点创建成功!"); zkClient.close(); } private static void readTest() { ZkClient zkClient = new ZkClient(zkAddress); try { System.out.println(zkClient.readData(testNode));//没有认证信息,读取会出错 } catch (Exception e) { System.err.println(e.getMessage()); } try { zkClient.addAuthInfo(digest, adminAuth.getBytes()); System.out.println(zkClient.readData(testNode));//admin权限与read权限不匹配,读取也会出错 } catch (Exception e) { System.err.println(e.getMessage()); } try { zkClient.addAuthInfo(digest, readAuth.getBytes()); System.out.println(zkClient.readData(testNode));//只有read权限的认证信息,才能正常读取 } catch (Exception e) { System.err.println(e.getMessage()); } zkClient.close(); } private static void writeTest() { ZkClient zkClient = new ZkClient(zkAddress); try { zkClient.writeData(testNode, "new-data");//没有认证信息,写入会失败 } catch (Exception e) { System.err.println(e.getMessage()); } try { zkClient.addAuthInfo(digest, writeAuth.getBytes()); zkClient.writeData(testNode, "new-data");//加入认证信息后,写入正常 } catch (Exception e) { System.err.println(e.getMessage()); } try { zkClient.addAuthInfo(digest, readAuth.getBytes()); System.out.println(zkClient.readData(testNode));//读取新值验证 } catch (Exception e) { System.err.println(e.getMessage()); } zkClient.close(); } private static void deleteTest() { ZkClient zkClient = new ZkClient(zkAddress); //zkClient.addAuthInfo(digest, deleteAuth.getBytes()); try { //System.out.println(zkClient.readData(testNode)); zkClient.delete(testNode); System.out.println("节点删除成功!"); } catch (Exception e) { System.err.println(e.getMessage()); } zkClient.close(); }// private static void deleteTest2() throws IOException, InterruptedException, KeeperException {// //使用zookeeper原生的API进行删除(注:delete权限指有没有权限删除子节点)// ZooKeeper zk = new ZooKeeper(zkAddress, 300000, new DemoWatcher());// zk.delete(testNode, -1);// System.out.println("节点删除成功");// zk.close();// }//// static class DemoWatcher implements Watcher {// @Override// public void process(WatchedEvent event) {// System.out.println("----------->");// System.out.println("path:" + event.getPath());// System.out.println("type:" + event.getType());// System.out.println("stat:" + event.getState());// System.out.println("<-----------");// }// } private static void changeACLTest() { ZkClient zkClient = new ZkClient(zkAddress); //注:zkClient.setAcl方法查看源码可以发现,调用了readData、setAcl二个方法 //所以要修改节点的ACL属性,必须同时具备read、admin二种权限 zkClient.addAuthInfo(digest, adminAuth.getBytes()); zkClient.addAuthInfo(digest, readAuth.getBytes()); try { List<ACL> acls = new ArrayList<ACL>(); acls.add(new ACL(ZooDefs.Perms.ALL, new Id(digest, DigestAuthenticationProvider.generateDigest(adminAuth)))); zkClient.setAcl(testNode, acls); Map.Entry<List<ACL>, Stat> aclResult = zkClient.getAcl(testNode); System.out.println(aclResult.getKey()); } catch (Exception e) { System.err.println(e.getMessage()); } zkClient.close(); } public static void main(String[] args) throws Exception { initNode(); System.out.println("---------------------"); readTest(); System.out.println("---------------------"); writeTest(); System.out.println("---------------------"); changeACLTest(); System.out.println("---------------------"); deleteTest(); //deleteTest2(); }} |
输出结果:

test-data
节点创建成功!
---------------------
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /test
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /test
test-data
---------------------
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /test
new-data
---------------------
[31,s{'digest,'admin-user:mAlW21Phn07yOvWnKJYq2sCMoZw=}
]
---------------------
节点删除成功!

从zkclient的使用结果看,与cli操作效果一样。
最后:关于多级节点之间的ACL,并非继承关系,但是也有些一联系,这是初次接触ACL中比较难理解的地方:

从这张图上可以发现,子节点/a/b的控制权限范围(全世界都能做任何事)可以超出父节点的范围(仅限:user-a:pwd:a具有read/admin权限)

继续,看上面的这4条红线标注的地方,从上向下一个个解释:
红线1:因为/a只有user-a:pwd-a有ra权限,即:没用户具有c(create)权限,所以不能创建子节点
红线2:因为/a/b为world:anyone:cdrwa权限,即无限制,所以在/a/b下创建子节点b1,地球人已经无法阻止,创建成功
红线3:给/a/b/b1指定了user-b1:pwd-b1的da权限(即:delete+admin)
(注:重温下前面提到的setAcl 二种模式,
一种是setAcl /path digest:username:encrypedpwd:crwda 用这种方式时,encrypedpwd用户必须是密文,
另一种方式是先addauth digest:usrname:password 先把授权信息加入上下文,这里password用的是明文,然后再setAcl /path auth:username:password:crdwa
所以如果在cli控制台测试,强烈建议用第二种方式,否则象上图中的方式用错了方式,pwd-b1在zk中被认为是密文,要解密出来几乎不可能,所以设置后,相当于这个节点就废了,因为你不知道密码,要操作该节点时,提供不了正确的认证信息)
红线4:还是刚才的理由,因为/a/b为world:anyone:cdrwa,没有限制,所以删除其下的子节点不受阻挡。

从上图可以看出,无法get父节点的内容,但是可以get子节点的内容,再次说明父、子节点的权限没直接关系,但是做delete时,上面的例子却遇到了麻烦:

想删除/a/b时,由于父节点/a的ACL列表里,只有ra权限,没有d权限,所以无法删除子节点。想删除/a时,发现下面还有子节点b,节点非空无法删除,所以这个示例就无解了(因为根据前面的操作,密码也还原不出来,也就无法修改ACL属性),而根节点/也无法删除,解决办法,只能到data目录里清空所有数据,再重启zk,但是这样就相当于所有数据全扔了,所以在设计ACL时,对于delete权限,要谨慎规划,在测试zk集群上做好测试,再转到生产环境操作。
最后给一些权限组合的测试结果:
要修改某个节点的ACL属性,必须具有read、admin二种权限
要删除某个节点下的子节点,必须具有对父节点的read权限,以及父节点的delete权限
摘自:http://www.cnblogs.com/yjmyzz/p/zookeeper-acl-demo.html
https://ihong5.wordpress.com/2014/07/24/apache-zookeeper-setting-acl-in-zookeeper-client/
http://zookeeper.apache.org/doc/r3.4.6/zookeeperProgrammers.html#sc_ZooKeeperAccessControl
原文链接:http://yjmyzz.cnblogs.com
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
【分布式】ZooKeeper权限控制之ACL(Access Control List)访问控制列表的更多相关文章
- ZooKeeper 笔记(5) ACL(Access Control List)访问控制列表
zk做为分布式架构中的重要中间件,通常会在上面以节点的方式存储一些关键信息,默认情况下,所有应用都可以读写任何节点,在复杂的应用中,这不太安全,ZK通过ACL机制来解决访问权限问题,详见官网文档:ht ...
- [转载] Zookeeper中的 ACL(Access Control List)访问控制列表
zk做为分布式架构中的重要中间件,通常会在上面以节点的方式存储一些关键信息,默认情况下,所有应用都可以读写任何节点,在复杂的应用中,这不太安全,ZK通过ACL机制来解决访问权限问题,详见官网文档:ht ...
- ACL(Access Control List)
一.ACL的简介 ACL(Access Control List 访问控制列表)是路由器和交换机接口的指令列表,用来控制端口进出的数据包.ACL的定义也是基于每一种被动路由协议的,且适用于所有的被动路 ...
- java zookeeper权限控制ACL(digest,IP)
java zookeeper权限控制ACL(auth,digest,ip) 学习前请参考:https://www.cnblogs.com/zwcry/p/10407806.html zookeeper ...
- Oracle ACL (Access Control List)详解
在Oracle11g中,Oracle在安全方面有了很多的改进,而在网络权限控制方面,也有一个新的概念提出来,叫做ACL(Access Control List), 这是一种细粒度的权限控制.在ACL之 ...
- windows访问控制列表 --ACL(Access Control List)
1.定义 ACL是一个windows中的表示用户(组)权限的列表. Access Control List(ACL) Access Control Entry(ACE) ... 2.分类 ACL分为两 ...
- ccna 闫辉单臂路由 和 acl access control list
ccna 闫辉单臂路由 和 acl access control list 一单臂路由 当前园区网设计很少用到 成本低 小型的.局域网可用 二ACL acc ...
- FreeIPA ACI (Access Control Instructions) 访问控制说明
目录 FreeIPA ACI (Access Control Instructions) 访问控制说明 一.ACI 位置 二.ACI 结构 三.ACI 局限性 四.复制拓扑中的ACI 五.操作ACI ...
- zookeeper ACL(access control lists)权限控制
基本作用: 针对节点可以设置 相关读写等权限,目的为了保障数据安全性 权限permissions可以制定不同的权限范围以及角色 一:ACL构成 zk的acl ...
随机推荐
- docker+nginx搭建tomcat集群(附录)——nginx.conf文件
附录:nginx.conf修改后的文件内容 user root;worker_processes 2; #error_log logs/error.log;#error_log logs/error. ...
- 【数据结构&算法】05-线性表之数组
目录 前言 线性结构与非线性结构 数组 数组的两个限制 数组的随机访问特性 数组的操作 插入操作 删除操作 数组越界 容器 数组下标 前言 本笔记主要记录数组的一些基础特性及操作. 顺便解答下为什么大 ...
- shell脚本 PHP+swoole的安装
#!bin/bash set -e # Check if user is root if [ $(id -u) != "0" ]; then echo "Error: p ...
- .Net Minimal Api 介绍
Minimal API是.Net 6中新增的模板,借助C# 10的一些特性以最少的代码运行一个Web服务.本文脱离VS通过VS Code,完成一个简单的Minimal Api项目的开发. 创建项目 随 ...
- css盒模型简介
如何了解盒模型 盒模型简介:盒模型是css布局的基石,它规定了网页元素如何显示以及元素间相互关系.css定义所有的元素都可以拥有像盒子一样的外形和平面空间. 盒模型的组成:内容区.补白/填充.边框.边 ...
- pipeline学习
目录 一.常用语法 二.基础使用 三.使用 Groovy 沙盒 四.参数化构建过程 五.pipeline script from SCM 六.参考 一.常用语法 1.拉取git仓库代码 checkou ...
- Netty数据如何在 pipeline 中流动
前言 在之前文章中,我们已经了解了pipeline在netty中所处的角色,像是一条流水线,控制着字节流的读写,本文,我们在这个基础上继续深挖pipeline在事件传播 Unsafe对象 顾名思义,u ...
- SpringCloud升级之路2020.0.x版-34.验证重试配置正确性(1)
本系列代码地址:https://github.com/JoJoTec/spring-cloud-parent 在前面一节,我们利用 resilience4j 粘合了 OpenFeign 实现了断路器. ...
- Emmet快速语法—助力HTML/CSS一行代码一个页面
学会之后牛掰的场景如下 我们的目标就是用一行代码=>写下面这样的长长长长的HTML结构来. 如:table>(thead.text>th{手机1}*4)+(tbody.text$*4 ...
- ES6-正则新增(复习+学习)
ES6-正则 昨天,复习了正则的基本知识,今天学习ES6新增的正则的知识,做一个总结笔记,大家可以先看4,5对应的方法然后再从头看,话不多说直接上: 1.RegExp构造函数的区别 2.新增的修饰符 ...