Zookeeper ACL

简介

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初遇

转载于:https://my.oschina.net/u/2474629/blog/912861

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值