文档章节

Zookeeper ACL

trayvon
 trayvon
发布于 2017/06/01 22:02
字数 1870
阅读 65
收藏 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
粉丝 15
博文 124
码字总数 184644
作品 1
程序员
ZooKeeper学习第六期---ZooKeeper机制架构

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

卯金刀GG
2017/10/25
0
0
ZooKeeper开发手册中文翻译

本文Github地址:https://github.com/sundiontheway/zookeeper-guide-cn 本文假设你已经具有一定分布式计算的基础知识。你将在第一部分看到以下内容: ZooKeeper数据模型 ZooKeeper Sessions...

__Steve__
2014/11/19
0
16
ZooKeeper学习笔记三 ZooKeeper与Paxos

本文学习内容来自: 《从Paxos到ZooKeeper分布式一致性原理与实践》 电子工业出版社 Apache ZooKeeper是由Apache Hadoop的子项目发展而来,于2010年11月正式成为了Apache的顶级项目。ZooKeep...

xundh
04/27
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

没有更多内容

加载失败,请刷新页面

加载更多

gson1.7.1线程并发导致空指针问题

java.lang.NullPointerExceptionat com.google.gson.FieldAttributes.getAnnotationFromArray(FieldAttributes.java:231)at com.google.gson.FieldAttributes.getAnnotation(FieldAttribut......

东风125
33分钟前
1
0
以太坊RPC接口使用

以太坊RPC接口文档: https://github.com/ethereum/wiki/wiki/JSON-RPC#web3_clientversion 使用方式: 比如我要调用某个合约的balanceOf(address _owner)方法。 因为没有改变合约的状态,所以...

王坤charlie
59分钟前
2
0
C#下的一个好用的日历库(sxtwl_cpp),支持农历转公历,和公历转农历等功能

sxtwl_cpp是寿星天文历的C++版本实现。支持多种语言的绑定 代码首页 懒人包 懒人包使用方法 1、右链工程中的引用-》添加引用-》浏览-》选中dotnet目录下的sxtwl.net.dll 2、生成解决方案-》找...

元谷
59分钟前
1
0
C++基础知识

链接:https://zhuanlan.zhihu.com/p/38399566 本文主要提一下以下三个区别: 引用必须初始化,而指针可以不初始化。 我们在定义一个引用的时候必须为其指定一个初始值,但是指针却不需要。 ...

悲催的古灵武士
今天
1
0
Oracle备份脚本,保留10天数据

@echo off echo 删除10天前的备分文件和日志forfiles /p "D:\oracleback\backfile" /m *.dmp /d -10 /c "cmd /c del @path" forfiles /p "D:\oracleback\backfile" /m *.log /d -10......

lyle_luo
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部