文档章节

Zookeeper ACL

trayvon
 trayvon
发布于 2017/06/01 22:02
字数 1870
阅读 80
收藏 0

简介

Zookeeper安装与配置可以参考Zookeeper初遇

zookeeper本身提供了ACL机制,表示为scheme:id:permissions 第一个字段表示采用哪一种机制,第二个id表示用户,permissions表示相关权限 zookeeper提供了如下几种机制(scheme) 1. world: 它下面只有一个id, 叫anyone, world:anyone代表任何人,zookeeper中对所有人有权限的结点就是属于world:anyone的 2. auth: 它不需要id, 只要是通过authentication的user都有权限(zookeeper支持通过kerberos来进行authencation, 也支持username/password形式的authentication) 3. digest: 它对应的id为username:BASE64(SHA1(password)),它需要先通过username:password形式的authentication 4. ip: 它对应的id为客户机的IP地址,设置的时候可以设置一个ip段,比如ip:192.168.1.0/16, 表示匹配前16个bit的IP段 5. x509

Zookeeper根目录/的权限是任何用户都有create,delete,read,write,admin(cdrwa) admin权限是设置ACL的权限。

zk 根目录ACL

如上图所示是zookeeper根目录/的ACL

注意:后面的zookeeper可能代指2个不同的东西,一个是zookeeper本身,另一个是org.apach.zookeeper包。

注意:znode是没有继承关系的,也就是说如果没有父目录如果有子目录权限可以直接访问子目录

WORLD

先来一个小例子:

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.zookeeper.ZooDefs.Perms;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.junit.Test;

public class ZookeeperWorldACLTest {
    

    private static final String WORLD = "world";

    @Test
    public void testWorldSchema(){
        List<ACL> acls = getWorldACL();
        ZooKeeper zookeeper = null;
        try {
            zookeeper = new ZooKeeper("localhost:2181",3000,null);
        } catch (IOException e) {
            e.printStackTrace();
        }
        create(zookeeper,acls);
        getData(zookeeper,acls);
        
    }
    
    private void create(ZooKeeper zookeeper,List<ACL> acls){
        try {
            String actualPath = zookeeper.create("/world", "world scheme".getBytes(), acls, CreateMode.EPHEMERAL);
            System.out.println(actualPath);
            actualPath = zookeeper.create("/world/xxx", "violate".getBytes(), acls, CreateMode.PERSISTENT);
            System.out.println(actualPath);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    private void getData(ZooKeeper zookeeper,List<ACL> acls){
        try {
            byte[] data = zookeeper.getData("/world", false, null);
            System.out.println(new String(data));
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    private List<ACL> getWorldACL(){
        Id id = new Id(WORLD,"anyone");//id = Ids.ANYONE_ID_UNSAFE;
        ACL acl = new ACL(Perms.READ,id);
        ArrayList<ACL> acls = new ArrayList<ACL>();
        acls.add(acl);
        return acls;
    }
    

}

下面是上面测试的输出:

/world
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /world/xxx
    at org.apache.zookeeper.KeeperException.create(KeeperException.java:104)
    at org.apache.zookeeper.KeeperException.create(KeeperException.java:42)
    at org.apache.zookeeper.ZooKeeper.create(ZooKeeper.java:637)
    at cn.freemethod.zk.ZookeeperWorldACLTest.create(ZookeeperWorldACLTest.java:38)
    at cn.freemethod.zk.ZookeeperWorldACLTest.testWorldSchema(ZookeeperWorldACLTest.java:29)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
world scheme

首先看在getWorldACL方法中的

Id id = new Id(WORLD,"anyone");//id = Ids.ANYONE_ID_UNSAFE;
ACL acl = new ACL(Perms.READ,id);

zookeeper包把zookeeper中的scheme:id:permissions的scheme:id封装成为了org.apache.zookeeper.data.Id,权限封装成为了Perms。每一个scheme下的id都不一样,甚至可能没有id。 上面的就使用的是world scheme(注意不是schema)获取的ACL,表示任何用户都有读取的权限。那么问题来了,为什么第一次创建可以成功呢?

那是因为第一次创建"/world"的路径是在根目录/下,而根目录/是所有用户都有权限的。所以创建成功了。第二次创建以为ACL只有"/world"的read权限,所以抛出了异常。

注意:创建目录的时候,父目录必须存在

DIGEST

老规矩,还是先来一个小例子:

import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;
import org.junit.Before;
import org.junit.Test;


public class ZookeeperDigestACLTest {
    
    private static final String DIGEST = "digest";

    private ZooKeeper zookeeper;

    @Before
    public void setUp() {
        try {
            zookeeper = new ZooKeeper("127.0.0.1:2181", 3000, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    @Test
    public void testDigestACL(){
        try {
            String actualPath = zookeeper.create("/digest", "digest".getBytes(), getDigestACL(), CreateMode.EPHEMERAL);
            System.out.println(actualPath);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    @Test
    public void testGetDigestSchemaData(){
        ZooKeeper zookeeper = getDigestZK();
        try {
            byte[] data = zookeeper.getData("/digest", false, null);
            System.out.println(new String(data));
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    List<ACL> getDigestACL(){
        List<ACL> acls = new ArrayList<ACL>();
        Id id = null;
        try {
            //设置ACL的时候使用密文
            String sid = DigestAuthenticationProvider.generateDigest("admin:admin");
            System.out.println(sid);
            id = new Id(DIGEST,sid);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        ACL acl = new ACL(ZooDefs.Perms.ALL, id);
        acls.add(acl);
        return acls;
    }
    
    ZooKeeper getDigestZK(){
        ZooKeeper zk = null;
        try {
            zk = new ZooKeeper("localhost:2181",2000, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //连接auth的时候使用明文
        zk.addAuthInfo(DIGEST, "admin:admin".getBytes());
        return zk;
    }
}

digest其实就是一个加密认证,主要需要注意的是设置认证的时候是使用的密文,可以通过zookeeper包的DigestAuthenticationProvider.generateDigest来获取,认证的时候是使用的明文。digest它对应的id为username:BASE64(SHA1(password)) 我们可以看一下DigestAuthenticationProvider.generateDigest的源码就知道了:

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

知道是使用base64对sha1加密后的数据进行编码,我们也可以很容易的自己实现,下面就是使用Apache的codec实现和DigestAuthenticationProvider.generateDigest的对比:

@Test 
    public void printPass(){
        try {
            String pass = DigestAuthenticationProvider.generateDigest("admin:admin");
            System.out.println(pass);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        byte[] sha1 = DigestUtils.sha1("admin:admin");
        String result = Base64.encodeBase64String(sha1);
        System.out.println("admin:"+result);
    }

auth

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.junit.Before;
import org.junit.Test;

public class ZookeeperAuthACLTest {
    
    private static final String DIGEST = "digest";
    
    private static final String AUTH = "auth";

    ZooKeeper zookeeper;

    @Before
    public void setUp() {
        try {
            zookeeper = new ZooKeeper("127.0.0.1:2181", 3000, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    @Test
    public void testAuthACL(){
//      ZooKeeper zookeeper = getDigestZK();
        ZooKeeper zookeeper = getAnotherDigestZK();
        try {
            String actualPath = zookeeper.create("/auth", "auth".getBytes(), getAuthACL(), CreateMode.EPHEMERAL);
            System.out.println(actualPath);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    @Test
    public void testAuthGet(){
        ZooKeeper zk = getAnotherDigestZK();
//      ZooKeeper zk = getDigestZK();
        try {
            byte[] data = zk.getData("/auth", false, null);
            System.out.println(new String(data));
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    List<ACL> getAuthACL(){
        List<ACL> acls = new ArrayList<ACL>();
        Id id = new Id(AUTH,"");
        ACL acl = new ACL(ZooDefs.Perms.ALL, id);
        acls.add(acl);
        return acls;
    }
    
    ZooKeeper getDigestZK(){
        ZooKeeper zk = null;
        try {
            zk = new ZooKeeper("localhost:2181",2000, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //连接auth的时候使用明文
        zk.addAuthInfo(DIGEST, "admin:admin".getBytes());
        return zk;
    }
    
    ZooKeeper getAnotherDigestZK(){
        ZooKeeper zk = null;
        try {
            zk = new ZooKeeper("localhost:2181",2000, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //连接auth的时候使用明文
        zk.addAuthInfo(DIGEST, "xxx:xxx".getBytes());
        return zk;
    }
}

auth和digest差不多auth也需要使用digest认证,不同的是创建znode节点和访问znode节点的必须是相同的认证。所以使用auth scheme的时候创建节点的时候就必须把认证加上,否则就会出错。

IP

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Id;
import org.junit.Before;
import org.junit.Test;

public class ZookeeperIPACLTest {
    
    private static final String IP = "ip";

    ZooKeeper zookeeper;

    @Before
    public void setUp() {
        try {
            zookeeper = new ZooKeeper("127.0.0.1:2181", 3000, null);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    @Test
    public void testIPACL(){
        try {
            String actualPath = zookeeper.create("/ip", "ip".getBytes(), getIPACL(), CreateMode.EPHEMERAL);
            System.out.println(actualPath);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    @Test
    public void testIPGet(){
        try {
            byte[] data = zookeeper.getData("/ip", false, null);
            System.out.println(new String(data));
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    List<ACL> getIPACL(){
        List<ACL> acls = new ArrayList<ACL>();
//      Id id = new Id(IP,"127.0.0.1");
        Id id = new Id(IP,"192.168.1.255");
//      Id id = new Id(IP,"192.168.1.0/24");
        ACL acl = new ACL(ZooDefs.Perms.ALL, id);
        acls.add(acl);
        return acls;
    }
}

ip scheme是通过客户端ip来过滤权限的。

附录

log4j配置

因为org.apache.zookeeper使用的是log4j,所以可以配置一个日志输出文件,可以获取一些Zookeeper的信息。比如当使用ZooKeeper不设置Watcher的时候就会抛出异常,但是这个异常是在ZooKeeper中处理的,所以如果不配做log4j是获取不到这些错误信息的。

#log4j.rootLogger  =   [ level ]   ,  appenderName1 ,  appenderName2 
#输出到控制台
#log4j.rootLogger=INFO,CONSOLE
#输出到文件
log4j.rootLogger=INFO,F
  
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender  
log4j.appender.CONSOLE.Threshold=INFO  
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout  
log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %m%n  
  
  
log4j.appender.F=org.apache.log4j.DailyRollingFileAppender  
log4j.appender.F.Threshold=INFO
#可以修改配置日志输出目录
log4j.appender.F.File=F:/log/zookeeper.log  
log4j.appender.F.DatePattern='.'yyyy-MM-dd  
  
log4j.appender.F.layout=org.apache.log4j.PatternLayout  
log4j.appender.F.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss}] - %m%n  
  
log4j.logger.org.displaytag=WARN  
log4j.logger.org.apache.zookeeper=ERROR  
log4j.logger.org.springframework=WARN  
log4j.logger.org.I0Itec=WARN

pom

上面zookeeper包的版本使用的是3.3.3,commons-codec是1.10

<dependencies>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                </exclusion>
                <exclusion>
                    <artifactId>slf4j-log4j12</artifactId>
                    <groupId>org.slf4j</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>
    </dependencies>

参考

ZooKeeper Zookeeper初遇

© 著作权归作者所有

共有 人打赏支持
trayvon
粉丝 14
博文 125
码字总数 185343
作品 1
程序员
私信 提问
ZooKeeper学习第六期---ZooKeeper机制架构

一、ZooKeeper权限管理机制  1.1 权限管理ACL(Access Control List)  ZooKeeper 的权限管理亦即ACL 控制功能,使用ACL来对Znode进行访问控制。ACL的实现和Unix文件访问许可非常相似:它使用...

卯金刀GG
2017/10/25
0
0
【分布式协调zookeeper】基础篇

一、zookeeper介绍 它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、名字服务、分布式同步、组服务等 zookeeper做了什么? 1.命名服务 2.配置管理 3.集群管理 4.分布式...

次渠龙哥
06/26
0
0
zookeeper conceptual

Zookeeper 数据模型 Zookeeper 拥有着类似文件系统的命名空间,唯一的区别是在命名空间下的每一个节点都有数据和他关联 Znodes zookeeper 的节点 znode, znode 维护着一些列状态信息 包含数据...

triankg
2014/01/13
0
0
zookeeper java客户端使用

ZooKeeper是一个优秀的分布式协同工具,很多分布式项目都基于它进行架构设计,不过要想要对其有一个深入的理解(如果你想阅读其源代码),对其客户端API的熟悉必不可少。下面就简要记录一下Z...

我就是我110
2015/08/13
0
0
zookeeper入门(3)API常用函数功能与参数详解

《zooker入门系列教程》: 1 - 如何在单机上实现ZooKeeper伪机群/伪集群部署 2 - 解读zookeeper的配置项 1. zookeeper初始化 功能: 创建一个句柄(handle)和一个响应(response)这个句柄的...

晨曦之光
2012/04/24
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

mybatis学习(1)

JDBC连接方式: 1.底层没有使用连接池,操作数据库需要频繁的创建和关闭连接,消耗资源。 2.写原生的JDBC代码在JAVA中,一旦需要修改SQL的话(比如表增加字段),JAVA需要整体重新编译,不利...

杨健-YJ
15分钟前
0
0
怎么组织文档

可以从以下几个方面考虑组织文档: ☐ 各种分支的界面截图和对应的类及文件 ☐ 框架或类图 ☐ 流程图 ☐ 时序图 ☐ 注意事项

-___-
27分钟前
1
0
分布式之数据库和缓存双写一致性方案解析

引言 为什么写这篇文章? 首先,缓存由于其高并发和高性能的特性,已经在项目中被广泛使用。在读取缓存方面,大家没啥疑问,都是按照下图的流程来进行业务操作。 但是在更新缓存方面,对于更...

别打我会飞
29分钟前
5
0
我的oracle11G,12c OCM之路

ocm认证感悟 ---------------------- 距离拿到ocm证书已经过了1年的时间,当初拿到证书的心情到现在还记得。其实在每个DBA心里都有一个成为强者的梦想,需要被认可,我也一样。我干过开发,做...

hnairdb
30分钟前
1
1
手动部署kubernetes集群(1.13.1最新版)

一、机器规划 使用五台机子部署k8s集群,规划如下: master节点3台(同时也是etcd节点) node节点2台 ip分配如下: ip:192.168.10.101,主机名:k8s-etcd01 ip:192.168.10.102,主机名:k8s...

人在艹木中
35分钟前
28
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部