ZooKeeper通过ACL来对ZNode进行访问控制。ZooKeeper客户端为ZNode指定ACL列表,ZooKeeper服务器根据ACL列表判定某个请求ZNode的客户端是否有对应操作的权限。
一个ACL对象由Permissions和Ids两部分组成。
Permissions用一组权限位(permission bits)来表示对应的权限,0表示无权限,1表示有权限。ZooKeeper中有5种权限,从低位到高位分别是READ、WRITE、CREATE、DELETE和ADMIN,它们的含义是:
* READ: 允许获取该节点的值和列出子节点。
* WRITE: 允许设置该节点的值。
* CREATE: 允许创建子节点。
* DELETE: 可以删除子节点。
* ADMIN: 允许为该节点设置权限。
ACL的Permissions可以是以上5种权限中的1种或多种,例如org.apache.zookeeper.ZooDefs.Perms定义的ALL类型,就是将以上5种权限做个‘或’运算,表示拥有所有权限。
1 2 3 4 5 6 7 8 |
public interface Perms { int READ = 1 << 0; int WRITE = 1 << 1; int CREATE = 1 << 2; int DELETE = 1 << 3; int ADMIN = 1 << 4; int ALL = READ | WRITE | CREATE | DELETE | ADMIN; } |
ZooKeeper中,ZNode没有Owner的概念,那些拥有ADMIN权限的客户端即等价于Owner,通过改变ZNode的ACL来进行权限管理。
如果说Permissions定义了ZNode有哪些操作可以做,那么Ids定义了ZNode有哪些人可以做。Ids由两部分组成:schema和expression,共同标识了一个允许接入的ZooKeeper客户端。schema用于区分客户端的类型,expression表示该类型下客户端的标识。ZooKeeper有内置的schema,用户也可以自定义。内置的schema有:
* world: world schema下只有一个expression: anyone。表示任何人。
* auth: auth schema下的expression为空。表示任何已经通过认证的客户端。
* digest: digest schema下的expression格式为username:password,相当于为ZNode指定一个“用户名+密码”,其他客户端要行使相应权限时,需要事先验证“用户名+密码”。服务器端为ZNode存储的认证信息为username:base64(SHA1(password))。
* ip: ip schema下的expression为CIDR格式的ip地址。
以下是一个利用digest schema验证的Demo:
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 |
// 初始化两类权限。admin拥有所有权限。guest只有写权限。 List<ACL> acls = new ArrayList<ACL>(); Id id1 = new Id("digest", DigestAuthenticationProvider.generateDigest("admin:admin")); ACL acl1 = new ACL(ZooDefs.Perms.ALL, id1); Id id2 = new Id("digest", DigestAuthenticationProvider.generateDigest("guest:guest")); ACL acl2 = new ACL(ZooDefs.Perms.WRITE, id2); acls.add(acl1); acls.add(acl2); // zk1用guest认证,创建/test ZNode。 // /test允许admin任意操作,允许guest只能进行写操作。 ZooKeeper zk1 = new ZooKeeper("127.0.0.1:2181", 2000, null); zk1.addAuthInfo("digest", "guest:guest".getBytes()); zk1.create("/test", "data".getBytes(), acls, CreateMode.PERSISTENT); // zk1尝试读取/test // ZNode节点值,抛org.apache.zookeeper.KeeperException$NoAuthException异常。 try { System.out.println("zk1: " + new String(zk1.getData("/test", false, null))); } catch (Exception e) { e.printStackTrace(); } // zk2用admin登陆,可以正常读取/test ZNode节点值。 ZooKeeper zk2 = new ZooKeeper("127.0.0.1:2181", 2000, null); zk2.addAuthInfo("digest", "admin:admin".getBytes()); try { System.out.println("zk2: " + new String(zk2.getData("/test", false, null))); } catch (Exception e) { e.printStackTrace(); } zk1.close(); zk2.close(); |