org.apache.zookeeper.KeeperException$InvalidACLException: KeeperErrorCode = InvalidACL 节点操作权限问题
遇到这个问题,就是操作某个节点时,ACL权限不足。或许是你没有登录具有该节点操作权限的用户;或许是你登录了,但是登录代码没有生效(可能是因为:登录代码和节点操作代码不在一个方法内),总之,该问题的解决,要围绕登录权限展开。如果找不到解决思路,先排查都有哪些用户具有该节点的操作权限吧,比如:#getAcl /succ/testDigest 登录具有权限的用户后,再对节点进行操作。
一、概述
节点不存在,正在创建…
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=
}
}
更多推荐
所有评论(0)