zk做为分布式架构中的重要中间件,通常会在上面以节点的方式存储一些关键信息,默认情况下,所有应用都可以读写任何节点,在复杂的应用中,这不太安全,ZK通过ACL机制来解决访问权限问题,详见官网文档:http://zookeeper.apache.org/doc/r3.4.6/zookeeperProgrammers.html#sc_ZooKeeperAccessControl
总体来说,ZK的节点有5种操作权限:
CREATE、READ、WRITE、DELETE、ADMIN 也就是 增、删、改、查、管理权限,这5种权限简写为crwda(即:每个单词的首字符缩写)
身份的认证有4种方式:
world:默认方式,相当于全世界都能访问
auth:代表已经认证通过的用户(当前app对应的已经登录OS用户?)
digest:即用户名:密码这种方式认证,这也是业务系统中最常用的
ip:使用Ip地址认证
Cli命令行下可以这样测试:
通过getAcl命令可以发现,刚创建的节点,默认是 world,anyone的认证方式,具有cdrwa所有权限
继续捣鼓:
先给/test,增加了增加了user1:12345的只读(r)权限控制,然后get /test时,提示认证无效,说明访问控制起作用了,但是不能理解的是,delete /test 却成功了。
代码使用:
zookeeper有一个很好用的客户端开源项目zkclient,官网地址为:http://github.com/zkclient ,其最新片0.7-dev已经支持ACL了,使用方法:
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参考下面设置:
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>
然后写一段代码测试一下:
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进行删除(测试结果:ACL好象也不起作用,不加任何ACL信息也能删除) // 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的使用结果看,对于delete,仍然不能理解的是,根本不用设置任何AuthInfo,也一样能删除成功,不明学厉,希望知道的朋友解惑,不胜感激。
ZooKeeper 笔记(5) ACL(Access Control List)访问控制列表
原文:http://www.cnblogs.com/yjmyzz/p/zookeeper-acl-demo.html