ZooKeeper 笔记(5) 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::r 设置权限,跟刚才的效果一样,但是密码这里输入的是明文,控制台模式下手动输入更方便。
好了,揭开加密规则:
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行
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参考下面设置:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>yjmyzz</groupId>
<artifactId>zkclient-demo</artifactId>
<version>1.0</version> <dependencies>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
</dependency> <dependency>
<groupId>com.101te</groupId>
<artifactId>zkclient</artifactId>
<version>0.7</version>
<classifier>dev</classifier>
</dependency> <dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency> </dependencies> </project>
然后写一段代码测试一下:
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权限
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
ZooKeeper 笔记(5) ACL(Access Control List)访问控制列表的更多相关文章
- [转载] Zookeeper中的 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的定义也是基于每一种被动路由协议的,且适用于所有的被动路 ...
- 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 ...
- Oracle ACL (Access Control List)详解
在Oracle11g中,Oracle在安全方面有了很多的改进,而在网络权限控制方面,也有一个新的概念提出来,叫做ACL(Access Control List), 这是一种细粒度的权限控制.在ACL之 ...
- Oracle ACL(Access Control List)
在oralce 11g中假如你想获取server的ip或者hostname,执行如下语句 SELECT utl_inaddr.get_host_address FROM dual; //获取IP S ...
- Thinking in Java,Fourth Edition(Java 编程思想,第四版)学习笔记(七)之Access Control
Access control ( or implementation hiding) is about "not getting it right the first time." ...
随机推荐
- 高性能 TCP & UDP 通信框架 HP-Socket v3.2.3
HP-Socket 是一套通用的高性能 TCP/UDP 通信框架,包含服务端组件.客户端组件和 Agent 组件,广泛适用于各种不同应用场景的 TCP/UDP 通信系统,提供 C/C++.C#.Del ...
- Tomcat下使用c3p0配置jndi数据源
下载c3p0包: 下载地址:https://sourceforge.net/projects/c3p0/files/?source=navbar 解压后得到包:c3p0-0.9.2.jar,mchan ...
- 关于移动端swiper的2种样式重置
手机查看效果地址:猛戳 ,PC端查看可以缩放浏览器窗口看效果~~ 思路:主要考虑选择器优先级大于默认就可以随意撸码 注意:该demo里用的mobile-adaptive.js是让页面以rem自适应,也 ...
- 深入理解Javascript中构造函数和原型对象的区别
在 Javascript中prototype属性的详解 这篇文章中,详细介绍了构造函数的缺点以及原型(prototype),原型链(prototype chain),构造函数(constructor) ...
- 利用CSS3D效果制作简易旋转木马效果
最近看一下css3d的一些特性,想着也实验学习一下,制作个小demo之类的.就练习了一下.开发一个粗糙的选择木马效果,如图 其实就是找到角度和位置,计算每根柱子的旋转角度摆放到3d空间的置顶位置即可. ...
- iOS开发需要哪些图片?
1.产品设计图 可以以6的长宽比 750*1134来设计.这些图片用于描述app是什么样子的,这是app开发人员的开发参考. 2.产品切图 将设计师将设计稿切成可以用来开发的图 颜色值 iOS颜色值取 ...
- For each循环中使用remove方法。
List<String> list =new ArrayList<String>(); list.add("boss"); list.add("g ...
- xp系统下硬盘安装centos6.5
引言: 电脑系统是Windows XP,电脑没有光驱.手头没有U盘.没有移动硬盘.电脑主板不支持U盘启动,在这种情况下想安装CentOS 6.0,有木有办法? 答案:有办法,请看下面教程! 必备工具: ...
- HTML5-03 页面布局
概述 HTML 文档中的元素是一个接着一个排列的,只是简单地在在块级元素的前后加上拆行,是一种流水布局.但是,我们所见到的 Web 页面按照一定的规则布局排版的(通常是多列的),所以就要借助一定的方法 ...
- Ubuntu下安装中文输入法
搜狗输入法 for Linux 是基于Fcitx 框架(fcitx-sogoupinyin). 安装环境为Ubuntu 13.04 安装过程: 卸载Ubuntu默认的ibus输入法: sudo apt ...