一、概述

节点不存在,正在创建…
Exception in thread "main" org.apache.zookeeper.KeeperException$InvalidACLException: KeeperErrorCode = InvalidACL for /succ/testdigest/childtest at org.apache.zookeeper.KeeperException.create(KeeperException.java:121)

 ACL :Access Control List 访问控制列表,用于控制资源的访问权限,类似于系统对文件的权限控制。

问题描述:

1、给用户lilei赋予节点“/succ/testdigest”的cdr操作权限;

2、通过前端代码登录lilei用户后,创建该节点的子节点时,提示上面的错误。

注:操作子节点时,首先要对父节点有操作权限,所以需要登录具有父节点操作权限的用户

问题根源:就是ACL的登录权限出问题了(要么没登录,要么登录没生效),别无他处。

解决办法:

1. 如果java客户端使用的原生态的zookeeper,尝试把登录代码紧挨着节点操作代码。 zookeeper.addAuthInfo("digest", "lilei:lilei".getBytes());//登录lilei用户

2.如果java客户端使用的是Curator,在实例化Client的时候,就需要登录有节点操作权限的用户。核心代码:.authorization("digest", "mayun:mayun".getBytes()) 

二、解决思路

1、关键字ACL,是权限问题。说明在操作某个节点时,权限存在不足。

2、脱离代码,难以说明问题,上代码!

三、我的代码片段(登录了,但是没生效)

1.创建节点的方法

  登录代码在这里才会生效,在main方法中不生效,很奇怪

public static void createNode(String nodePath,byte[]data,List<ACL> acls) throws Exception{
		ZooKeeper zookeeper=new ZKNodeAcl(zkServerPath).getZookeeper();
		Stat stat=zookeeper.exists(nodePath, false);//先判断节点是否已经存在(false意思是不再回调process)
		if(null!=stat) {
			System.out.println("节点已存在,版本号:"+stat.getVersion());
		}else {
			System.out.println("节点不存在,正在创建…");
			//zookeeper.addAuthInfo("digest", "lilei:lilei".getBytes());//登录lilei用户
			String result=zookeeper.create(nodePath, data, acls,CreateMode.PERSISTENT);
			System.out.println("创建节点:\t"+result+"\t成功");
		}
	}

注:zookeeper.addAuthInfo("digest", "lilei:lilei".getBytes());//登录lilei用户

这段代码稍后要放开,也就是说不要在main方法中登录(登录不会生效),在节点操作方法的上一行代码登录。 

2.单独赋予用户权限方法,返回用户权限ACLs列表

//同一个节点,分别给mayun和lilei两个用户分配不同的操作权限
	public static List<ACL> getAcls() throws NoSuchAlgorithmException{
		List<ACL> acls=new ArrayList<ACL>();
		Id mayun =new Id("digest", AclUtils.getDigestUserPwd("mayun:mayun"));
		Id lilei =new Id("digest", AclUtils.getDigestUserPwd("lilei:lilei"));
		acls.add(new ACL(Perms.ALL, mayun));//给mayun一次性赋值所有权限
		acls.add(new ACL(Perms.READ, lilei));
		acls.add(new ACL(Perms.DELETE | Perms.CREATE, lilei));//给lilei分两次赋权限(目的:看不同的赋权方式)
		return acls;
	}

3.main方法(问题所在,发现登录代码在这里没生效)

   public static void main(String[] args) throws Exception {
		ZKNodeAcl zkServer=new ZKNodeAcl(zkServerPath);
		//ZKNodeAcl.createNode("/succ/abc", "hello".getBytes(), Ids.OPEN_ACL_UNSAFE);//分配所有权限
		//ZKNodeAcl.createNode("/succ/testdigest", "testdigest".getBytes(), getAcls());//分配自定义权限
		//succ/testdigest节点已创建且已分配权限,如果要对这个节点操作,则需要先登录
		zkServer.getZookeeper().addAuthInfo("digest", "lilei:lilei".getBytes());//登录lilei用户(就是这个行代码,感觉是没有生效)
		zkServer.createNode("/succ/testdigest/childtest", "childtest".getBytes(), Ids.CREATOR_ALL_ACL);//赋予lilei对子节点所有权限
   	}
	

四、解决步骤

1.诡异的权限登录,明明在main中登录了,还是报错提示ACL权限问题

因在main方法中上一步操作,创建“/succ/testdigest”节点时,在getAcls()方法中给用户lilei分配了这个节点的cdr权限。

然而,需要给“/succ/testdigest”这个节点,创建子节点时,创建子节点前必须先登录具有该节点操作权限的用户。zk 后台代码是 #addauth digest lilei:lilei ,这段代码转换为前台代码就是:

zkServer.getZookeeper().addAuthInfo("digest", "lilei:lilei".getBytes()); 

如上代码,明明是已经登录了,还是报错。一顿度娘,无果,索性就把登录代码转移到创建节点代码的上一行,让创建子节点的代码与权限登录代码挨着

2.权限登录让其紧挨着创建子节点的代码

zookeeper.addAuthInfo("digest", "lilei:lilei".getBytes());//登录lilei用户
			String result=zookeeper.create(nodePath, data, acls,CreateMode.PERSISTENT);

再次运行,发现子节点操作成功了。

结论

登录代码和节点操作代码,如果不在一个方法中,会不生效,不确定是bug还是什么原因。

本案例中,登录代码在main方法中,通过main方法调用子节点创建方法createNode(),然后诡异的是,登录没能生效。把登录代码,转移到createNode()方法内部,就生效了。

 如果你的问题没有被解决,且客户端使用的是Curator继续看如下代码

这是一个类的构造方法,在代码中添加:.authorization("digest", "mayun:mayun".getBytes()),里面的mayun:mayun需要替换为你本地创建的具有操作权限的用户名和密码

public CuratorAcl() {
        RetryPolicy retryPolicy = new RetryNTimes(3, 5000);
        client = CuratorFrameworkFactory.builder().authorization("digest", "mayun:mayun".getBytes())
                .connectString(zkServerPath)
                .sessionTimeoutMs(10000).retryPolicy(retryPolicy)
                .namespace("workspace").build();
        client.start();
    }

需要注意的是:这个行代码,还有一个兄弟方法,可以一次性登录多个用户。 

尾言 

本案例不知可否解决你遇到的问题,但是希望能为你提供一种解决问题的思路。总之遇到这个问题,就是ACL权限问题。登录代码是少不了的,基本是出现了登录代码这个点。

如果找不到解决思路,先排查自己是否具有该节点的操作权限吧,比如:

#getAcl /succ/testDigest 查看都有哪些用户对该节点有操作权限,然后据上述代码登录后,再对节点进行操作。

附录:AclUtils.java

import java.security.NoSuchAlgorithmException;

import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;

public class AclUtils {
	public static String getDigestUserPwd(String idPassword) throws NoSuchAlgorithmException {
		return DigestAuthenticationProvider.generateDigest(idPassword);
	}
	public static void main(String[] args) throws NoSuchAlgorithmException {
		String idPassword="succ:succ";//该用户通过后台的#addauth digested succ:succ 命令来创建的
		String idDigested=getDigestUserPwd(idPassword);
		System.out.println(idDigested);
		//succ:xwbPSQtEd/X8NWYNr6QOFbTnJ3A=
	}
}

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐