开始之前先介绍一些Zookeeper的权限。zookeeper支持的权限有5种分别是
cdrwa:
create: 你可以创建子节点。
read: 你可以获取节点数据以及当前节点的子节点列表。
write: 你可以为节点设置数据。
delete: 你可以删除子节点。
admin: 可以为节点设置权限
刚刚搭建好的 zookeeper 集群没有任何权限
[zk: localhost:2181(CONNECTED) 11] getAcl /
'world,'anyone:rwadc #表示所有人都对这个节点有rwadc的权限
scheme介绍
world 表示所有。创建节点的默认权限。有唯一的id是anyone授权的时候的模式为world:anyone:rwadc表示所有人都对这个节点有rwadc的权限。这里用的是id而不是expression
auth 不需要id。不过这里应该用expression来表示。即(scheme:expression:perm)
digest 使用用户名:密码编码成md5的方式来作为访问控制列表的id。但是这里id不作为授权语句的一部分,这里也是用expression的方式。用户名: 密码先进行sha1编码后再用base64编码。这个比较恶心,后面再详细介绍。
host 使用用户主机名作为访问控制列表的id。但是这里需要注意的是表达式用的是主机名的后缀即可。举个例子。如果表达式设置为corp.com可以匹配如host1.corp.com,host2.corp.com的主机名,但是不能匹配host1.zookeeper.com这个主机名。
ip 跟主机名类似,这里用客户端的ip地址作为访问控制列表的id。表达式可以用addr/bits这种方式来设置ip白名单。
上面都是一些很官方的话语,由于懒得写就直接搬过来
下面开始介绍如何设置权限,这里只是讲auth与digest两种方式。
auth的授权方式
创建新的节点
[zk: localhost:2181(CONNECTED) 7] create /auth auth
Created /auth
授予auth权限,这里就有个坑,文档里面根本没有说明应该如何授予这个权限。
auth doesn't use any id, represents any authenticated user.
要他何用?也就是说文档没有说明该用何种expression来表示这个权限,后面经过翻阅资料查得,username:password的来作为expression。所以有了下面的的语法。
[zk: localhost:2181(CONNECTED)9] setAcl /auth auth:zookeeper:zookeeper:rwadcAclisnotvalid : /auth
从上面可以看到这里Acl不能识别。这里又是一个文档的不足的地方,我是后来翻阅博客才知道有下面这样的黑魔法
设置这个权限之前首先需要
[zk: localhost:2181(CONNECTED) 12]addauthdigestzookeeper:zookeeper
然后再设置auth
[zk: localhost:2181(CONNECTED) 13] setAcl /auth auth:zookeeper:zookeeper:rwadc
cZxid = 0x3
ctime = Mon May 30 10:32:15 CST 2016
mZxid = 0x3
mtime = Mon May 30 10:32:15 CST 2016
pZxid = 0x3
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 4
numChildren = 0
检查权限
'digest,'zookeeper:4lvlzsipXVaEhXMd+2qMrLc0at8=: cdrwa
可以看到权限已经设置成功了。
我也不知道怎么吐槽官方文档好了。冷暖自知。(T_T)
digest授权方式
这种方式跟auth的方式是差不多的,不同的在于
[zk: localhost:2181(CONNECTED) 2] create /digest digest
Created /digest
[zk: localhost:2181(CONNECTED) 3] setAcl /digest digest:zookeeper:zookeeper:rwadc
cZxid= 0x15
ctime = Mon May 30 10:59:24 CST 2016
mZxid = 0x15mtime = Mon May 30 10:59:24 CST 2016
pZxid = 0x15
cversion = 0
dataVersion = 0
aclVersion = 1
phemeralOwner = 0x0
dataLength = 6
numChildren = 0
[zk: localhost:2181(CONNECTED) 5] getAcl /digest'digest,'zookeeper:zookeeper: cdrwa
可以看出比较明显的:
设置之前不需要通过addauth添加对应的授权认证,而是直接授权便可。
命令行设置了什么,然后在获取对应的权限的时候就会把对应的字符串显式记录下来,而不会进行任何编码, 如上面的zookeeper:zookeeper。这种方式就需要自己去编码了。
这也是个比较坑爹的事情,设置密码之前需要把密码先自己进行sha1编码然后吧结果进行base64编码。如果编码的方式错误,你需要使用zookeeper的超级用户来重新建立新的用户用来操作该路径如何使用超级用户来创建新用户会另外写一篇文章
传送门: zookeeper 超级用户
下面演示正确的方法首先对想要的用户名:密码进行处理
第一种 linux:
# 正确的方式:
mcbadm@cm01 ~]$ echo -n zookeeper:zookeeper | openssl dgst -binary -sha1 | openssl base64
4lvlzsipXVaEhXMd+2qMrLc0at8=
错误的方式:
[mcbadm@cm01 ~]$ echo zookeeper:zookeeper| openssl dgst -binary -sha1 |openssl base64VWUr1lLcb0QrQUJmK+WEKUYYnV4=
错误的方式:
[mcbadm@cm01 ~]$ echo -nzookeeper:zookeeper| sha1sum |base64 ZDYyNGUwYmVmYmU2Yzg3NTUwNDg4OGVkMjA4NzBiYzU1NGY1ZDk1NCAgLQo=
第二种代码方式:
package com.ht;
import org.apache.zookeeper.KeeperException;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author zhu
* @Date 18-9-19 下午6:48
*/
public class Acl {
public static void main(String[] args)throws IOException, InterruptedException, KeeperException {
String passwd ="zookeeper:zookeeper";
System.out.println(generateDigest(passwd));
}
static public String generateDigest(String idPassword) {
String parts[] = idPassword.split(":",2);
byte digest[] =null;
try {
digest = MessageDigest.getInstance("SHA1").digest(idPassword.getBytes());
}catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return parts[0] +":" +base64Encode(digest);
}
static final private String base64Encode(byte b[]) {
StringBuilder sb =new StringBuilder();
for (int i =0; i < b.length; ) {
int pad =0;
int v = (b[i++] &0xff) <<16;
if (i < b.length) {
v |= (b[i++] &0xff) <<8;
}else {
pad++;
}
if (i < b.length) {
v |= (b[i++] &0xff);
}else {
pad++;
}
sb.append(encode(v >>18));
sb.append(encode(v >>12));
if (pad <2) {
sb.append(encode(v >>6));
}else {
sb.append('=');
}
if (pad <1) {
sb.append(encode(v));
}else {
sb.append('=');
}
}
return sb.toString();
}
static final private char encode(int i) {
i &=0x3f;
if (i <26) {
return (char) ('A' + i);
}
if (i <52) {
return (char) ('a' + i -26);
}
if (i <62) {
return (char) ('0' + i -52);
}
return i ==62 ?'+' :'/';
}
}
上面是我踩过的坑。
得出的结果可见,跟用auth方式添加权限之后getAcl得到的密码结果是一样的。然后再把这个结果作为密码用digest的方式存进去
[zk:localhost:2181(CONNECTED)18] setAcl /digestdigest:zookeeper:4lvlzsipXVaEhXMd+2qMrLc0at8=:rwdca
cZxid =0x1d
ctime = Mon May3011:12:54CST2016
mZxid =0x1dmtime = Mon May3011:12:54CST2016
pZxid =0x1
dcversion =0
dataVersion =0
aclVersion =1
ephemeralOwner =0x0
dataLength =6
numChildren =0
就可以通过addauth的方式访问了
[zk: localhost:2181(CONNECTED)0] get /digest
Authenticationisnotvalid : /digest
[zk: localhost:2181(CONNECTED)1] addauth digest zookeeper:zookeeper
[zk: localhost:2181(CONNECTED)2] get /digest
digest
cZxid =0x1d
time = Mon May3011:12:54CST2016
mZxid =0x1
dmtime = Mon May3011:12:54CST2016
pZxid =0x1
dcversion =0
ataVersion =0
aclVersion =1
ephemeralOwner =0x0
dataLength =6
numChildren =0
如果密码忘记了依然可以用超级用户找回使用权