总体来说,ZK的节点有5种操作权限:

CREATE、READ、WRITE、DELETE、ADMIN也就是增、删、改、查、管理权限,这5种权限简写为crwda。

注:

delete是指对子节点的删除权限,其它4种权限指对自身节点的操作权限。

 

Cli命令行下可这样测试:

  • create /test 'test-data'
  • getAcl /test

通过getAcl命令可以发现,刚创建的节点,默认是world, anyone的认证方式,具有cdrwa所有权限。

  • setAcl /test digest:user1:UdxDQl4f9v5oITwcAsO9bmWgHSI=:r
  • getAcl /test
  • addauth digest user1:super123
  • get /test
  • delete /test

先给/test增加了user1:UdxDQl4f9v5oITwcAsO9bmWgHSI=的只读r权限控制,密码必须是加密后的内容,对应的原文是super123。

然后get /test时,提示认证无效,说明访问控制不起作用,接下来:

addauth...给“上下文”增加一个认证用户,接着get /test就能取到数据了。

最后 delete /test 成功了!原因是:根节点/默认是world:anyone:crdwa,所以也就是说任何人,都能对根节点/进行读写创建子节点、管理acl,以及删除子节点。

 

刚才也提到了,setAcl /path digest,必须输入加密后的密码,这个 cli控制台上很不方便,所以常用下面的方式:

  • create /test 'test-data'
  • addauth digest user1:UdxDQl4f9v5oITwcAsO9bmWgHSI=
  • setAcl /test auth:user1:super123: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有一个很好用的客户端开源项目:http://github.com/zkclient

 

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();
 
    }
}

 

要修改某个节点的ACL属性,必须具有read、admin二种权限。

要删除某个节点下的子节点,必须具有对父节点的read权限,以及父节点的delete权限。

 

Zookeeper超级用户

Zookeeper管理员会因为某些客户端对某些节点设置了权限,而导致在紧急的情况下无法修改这些节点。在这种情况下,管理员可以通过zookeeper超级用户模式访问这些节点,一旦设置了超级权限访问节点,后续的操作就不需要check ACL了。

使用超级用户模式,可以通过zookeeper的zookeeper.DigestAuthenticationProvider.superDigest参数开启。

 

在zkServer.sh配置启动参数:

 

nohup "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" "-Dzookeeper.DigestAuthenticationProvider.superDigest=super:g9oN2HttPfn8MMWJZ2r45Np/LIA=" \


重新启动zookeeper服务

zkServer.sh restart

使用刚才设置的用户名就可以访问任意节点了及操作。

 

提醒,操作完成后,最好把zkServer.sh文件还原了,以免出安全问题。

 

最后欢迎大家访问我的个人网站:1024s

 

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐