ZooKeeper之Java客户端API使用—权限控制。

来源:互联网 发布:展会erp系统源码 编辑:程序博客网 时间:2024/05/16 16:02

         在ZooKeeper的实际使用中,我们的做法往往是搭建一个共用的ZooKeeper集群,统一为若干个应用提供服务。在这种情况下,不同的应用往往是不会存在共享数据的使用场景的,因此需要解决不同应用之间的权限问题。

        为了避免存储在ZooKeeper服务器上的数据被其他进程干扰或人为操作修改,需要对ZooKeeper上的数据访问进行权限控制(Access Control)。ZooKeeper提供了ACL的权限控制机制,简单的讲,就是通过设置ZooKeeper服务器上数据节点的ACL,来控制客户端对该数据节点的访问权限:如果iyge客户端符合该ACL控制,那么就可以对其进行访问,否则将无法操作。针对这样的控制机制,ZooKeeper提供了多种权限控制模式(Scheme),分别是world、auth、digest、ip和super。这里主要是在digest模式下如何进行ZooKeeper的权限控制。

        开发人员如果要使用ZooKeeper的权限控制功能,需要在完成ZooKeeper会话创建后,给该会话添加上相关的权限信息(AuthInfo)。ZooKeeper客户端提供了相应的API接口来进行权限信息的设置,如下:

addAuthInfo(String scheme, byte[] auth)

API方法的参数说明如下表所示。

参数名说明scheme权限控制模式,分为world、auth、digest、ip和superauth具体的权限信息

        该接口主要用于为当前ZooKeeper会话添加权限信息,之后凡是通过该会话对ZooKeeper服务端进行的任何操作,都会带上该权限信息。

使用包含权限信息的ZooKeeper会话创建数据节点

public class AuthSample {

final static String PATH = "/zk-book-auth_test";

public static void main(String[] args) throws Exception {

ZooKeeper zookeeper = new ZooKeeper("domain1.book.zookeeper:2181", 50000, null);

zookeeper.addAuthInfo("digest", "foo:true".getBytes());

zookeeper.create(PATH, "init".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.EPHEMERAL);

Thread.sleep(Integer.MAX_VALUE);

}

}

        上面这个示例程序就是一个典型的对ZooKeeper会话添加权限信息的使用方式。在这个示例中,我们采用了digest模式,同时可以看到其包含的具体权限信息是foo:true,这非常类似于username:password的格式。完成权限信息的添加后,该示例还使用客户端会话在ZooKeeper上创建了/zk-book-auth-test节点,这样该节点就受到了权限控制。下面我们来看,针对这个数据节点,ZooKeeper是如何进行权限控制的。

使用无权限信息的ZooKeeper会话访问含权限信息的数据节点

public class AuthSample_Get {

final static String PATH = "/zk-book-auth_test";

public static void main(String[] args) throws Exception {

ZooKeeper zookeeper1 = new ZooKeeper("domain1.book.zookeeper:2181", 50000, null);

zookeeper1.addAuthInfo("digest", "foo:true".getBytes());

zookeeper1.create(PATH, "init".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.EPHEMERAL);

ZooKeeper zookeeper2 = new ZooKeeper("domain1.book.zookeeper:2181", 50000, null);

zookeeper2.getData(PATH, false, null);

}

}

        运行程序,输出异常信息如下:

        在上面这个示例程序中,我们首先通过一个包含权限信息的客户端会话创建了一个数据节点,然后使用另一个不包含权限信息的客户端会话对其进行访问,运行程序后,输出了异常信息:KeeperErrorCode = NoAuth for /zk-book-auth_test。可见,一旦我们对一个数据节点设置了权限信息,那么其他没有权限设置的客户端会话将无法访问该数据节点。ZooKeeper服务端能够为我们实现权限控制。

使用错误权限信息的ZooKeeper会话访问含权限信息的数据节点

// 使用错误权限信息的ZooKeeper会话访问含权限信息的数据节点

public class AuthSample_Get2 {

final static String PATH = "/zk-book-auth_test";

public static void main(String[] args) throws Exception {

ZooKeeper zookeeper1 = new ZooKeeper("domain1.book.zookeeper:2181", 50000, null);

zookeeper1.addAuthInfo("digest", "foo:true".getBytes());

zookeeper1.create(PATH, "init".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.EPHEMERAL);

ZooKeeper zookeeper2 = new ZooKeeper("domain1.book.zookeeper:2181", 50000, null);

zookeeper2.addAuthInfo("digest", "foo:true".getBytes());
System.out.println(zookeeper2.getData(PATH, false, null ));

ZooKeeper zookeeper3 = new ZooKeeper("domain1.book.zookeeper:2181", 50000, null);

zookeeper3.addAuthInfo("digest", "foo:false".getBytes());

zookeeper3.getData(PATH, false, null);

}

}

        运行程序,输出结果如下:

        在上面这个示例程序中,我们同样使用包含权限信息的客户端会话创建了数据节点,同时使用了两个权限信息,先后进行了两次数据节点内容的获取。第一次,我们使用了正确的权限信息,即digest[foo:true],同时也成功获取到了数据节点的数据内容:[B@7b7072;而在第二次接口调用中,由于使用了错误的权限信息,即digest[foo:false],结果出现异常:KeeperErrorCode = NoAuth for /zk-book-auth_test。可见,ZooKeeper的权限控制也能够为我们识别出错误的权限信息。

        在ZooKeeper中,几乎所有的API接口操作,其权限控制策略都是和上面几个示例类似的,但是对于删除节点(delete)接口而言,其权限控制比较特殊。

删除节点接口的权限控制

// 删除节点接口的权限控制

public class AuthSample_Delete {

final static String PATH = "/zk-book-auth_test";

final static String PATH2 = "/zk-book-auth_test/child";

public static void main(String[] args) throws Exception {

ZooKeeper zookeeper1 = new ZooKeeper("domain1.book.zookeeper:2181", 50000, null);

zookeeper1.addAuthInfo("digest", "foo:true".getBytes());

zookeeper1.create(PATH, "init".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.PERSISTENT);

zookeeper1.create(PATH2, "init".getBytes(), Ids.CREATOR_ALL_ACL, CreateMode.EPHEMERAL);

try {

ZooKeeper zookeeper2 = new ZooKeeper("domain1.book.zookeeper:2181", 50000, null);
zookeeper2.delete(PATH2, -1);
} catch (Exception e) {
System.out.println("删除节点失败:" + e.getMessage());

}
ZooKeeper zookeeper3 = new ZooKeeper("domain1.book.zookeeper:2181", 50000, null);
zookeeper3.addAuthInfo("digest", "foo:true".getBytes());
zookeeper3.delete(PATH2, -1);
System.out.println("成功删除节点:" + PATH2);

ZooKeeper zookeeper4 = new ZooKeeper("domain1.book.zookeeper:2181", 50000, null);
zookeeper4.delete(PATH, -1);
System.out.println("成功删除节点:" + PATH);

}

}

        运行程序,输出结果如下:

         在上面这个示例程序中,第一次,我们使用没有包含权限信息的客户端会话进行数据节点删除操作,显然,程序运行程序中抛出了异常信息:KeeperErrorCode = NoAuth for /zk-book-auth_test/child。而在第二次接口调用中,由于使用了正确的权限信息,因此成功删除了数据节点,相信这也不难理解。

        下面我们着重来看第三次节点删除操作。需要注意的是,在这次删除操作中,我们使用的是没有包含权限信息的客户端会话,但最终却成功删除了数据节点。从这个例子中,我们可以看到,删除节点接口的权限控制比较特殊,当客户端对一个数据节点添加了权限信息后,对于删除操作而言,其作用范围是其子节点。也就是说,当我们对一个数据节点添加信息后,依然可以自由的删除这个节点,但是对于这个节点的子节点,就必须使用相应的权限信息才能够删除掉他。

原创粉丝点击