但是如果当该客户端对目标节点进行删除时,做 & 判断权限的话,可以得到结果为 0,表示该客户端不具备删除的权限,就会返回给客户端权限错误
int binary RWC 7 00111 D 8 & 01000 ------------------ 结果 0 00000 3.2 Scheme 介绍Scheme 有 4 种,分别是 ip、world、digest、super,但是其实就是两大类,一种是针对 IP 地址的 ip,另一种是使用类似“用户名:密码”的 world、digest、super。其实整个 ACL 是分三个部分的,scheme:id:perms ,id 的取值取决于 scheme 的种类,这里是 ip 所以 id 的取值就是具体的 IP 地址,而 perms 则是我上一小节介绍的 RWCDA。
这三部分的前两部分 scheme:id 相当于告诉服务端 “我是谁?”,而最后的部分 perms 则是代表了 “我能做什么?”,这两个问题,任意一个问题出错都会导致服务端抛出 NoAuthException 的异常,告诉客户端权限不够。
3.2.1 IP我们先来直接看一段代码,其中的 IP 10.11.12.13 我是随便写的
ZooKeeper client = new ZooKeeper("127.0.0.1:2181", 3000, null); List<ACL> aclList = new ArrayList<>(); aclList.add(new ACL(ZooDefs.Perms.ALL, new Id("ip", "10.11.12.13"))); String path = client.create("/abc", "test".getBytes(), aclList, CreateMode.PERSISTENT); System.out.println(path); // 输出 /abc client.close();可以看到 /abc 是可以被正确输出的,而且通过查看 / 的子节点列表是可以看到 /abc 节点的
ZooKeeper client = new ZooKeeper("127.0.0.1:2181", 3000, null); List<String> children = client.getChildren("http://www.likecs.com/", false); System.out.println(children); // 输出 [abc, zookeeper] client.close();但是现在如果去访问该节点的数据的话就会得到报错
ZooKeeper client = new ZooKeeper("127.0.0.1:2181", 3000, null); byte[] data = client.getData("/abc", false, null); System.out.println(new String(data)); client.close(); Exception in thread "main" org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /abc读者可以试试把上面的 IP 改成 127.0.0.1 重新创建节点,之后就能正常访问了,一般生产环境中 IP 模式用的不多(也可能是我用的不多),如果要用 IP 控制访问的话,通过防火墙白名单之类的手段即可,这个层面我认为不需要 ZK 去管。
3.2.2 World这个模式应该是最常用的(手动狗头)
我们还是来看一段代码
ZooKeeper client = new ZooKeeper("127.0.0.1:2181", 3000, null); List<ACL> aclList = new ArrayList<>(); aclList.add(new ACL(ZooDefs.Perms.READ, new Id("world", "anyone"))); // 区别是这行 String path = client.create("/abc", "test".getBytes(), aclList, CreateMode.PERSISTENT); System.out.println(path); // 输出 /abc client.close();我把 scheme 改成了 World 模式,而 World 模式的 id 取值就是固定的 anyone 不能用其他值,而且我还设置了 perms 为 R,所以这个节点只能读数据,但不能做其他操作,如果使用 setData 对其进行数据修改的话也会得到权限的错误
ZooKeeper client = new ZooKeeper("127.0.0.1:2181", 3000, null); Stat stat = client.setData("/abc", "newData".getBytes(), -1); // NoAuth for /abc现在再回头看之前的 ZooDefs.Ids.OPEN_ACL_UNSAFE,其实就是 ZK 提供的常用的静态常量,代表不校验权限
Id ANYONE_ID_UNSAFE = new Id("world", "anyone"); ArrayList<ACL> OPEN_ACL_UNSAFE = new ArrayList<ACL>(Collections.singletonList(new ACL(Perms.ALL, ANYONE_ID_UNSAFE))); 3.2.3 Digest这个就是我们熟悉的用户名密码了,还是先上代码
ZooKeeper client = new ZooKeeper("127.0.0.1:2181", 3000, null); List<ACL> aclList = new ArrayList<>(); aclList.add(new ACL(ZooDefs.Perms.ALL, new Id("digest", DigestAuthenticationProvider.generateDigest("laoxun:kaixin")))); // 1 String path = client.create("/abc", "test".getBytes(), aclList, CreateMode.PERSISTENT); System.out.println(path); client.close();这个写法中必须要注意的是 1 处的 username:password 的字符串必须通过 DigestAuthenticationProvider.generateDigest 的方法包装一下,用这个方法会对传入的字符串进行编码。
包装完后 laoxun:kaixin 其实变成了 laoxun:/xQjqfEf7WHKtjj2csJh1/aEee8=,这个过程如下:
laoxun:kaixin 对整个字符串先进行 SHA1 加密
对加密后的结果进行 Base64 编码
将用户名和编码后的结果拼接
上面的代码还有一种写法如下,使用 addAuthInfo 在客户端上下文中添加权限信息
ZooKeeper client = new ZooKeeper("127.0.0.1:2181", 3000, null); client.addAuthInfo("digest", "laoxun:kaixin".getBytes()); // 1. List<ACL> aclList = new ArrayList<>(); aclList.add(new ACL(ZooDefs.Perms.ALL, new Id("auth", ""))); // 2. 这里的 Id 是固定写法 String path = client.create("/abc", "test".getBytes(), aclList, CreateMode.PERSISTENT); System.out.println(path); client.close();