最近工作一般忙吧,利用工作之余学习学习zookeeper。
Zookeeper是一个分布式的、开源的分布式应用协调服务。这个介绍很简单吧?详情zk简介参照:http://zookeeper.majunwei.com/document/3.4.8/OverView.html。
中文参考文档:http://zookeeper.majunwei.com/document/3.4.8/。但是其开发者部分尚未完成翻译。
官方参考文档:http://zookeeper.apache.org/doc/trunk/。
还是那句老话,做什么事情都要讲究方法,授人以鱼不如授人以渔。首先就是阅读官方文档。当我看到ACL部分时(http://zookeeper.apache.org/doc/trunk/zookeeperProgrammers.html#sc_ZooKeeperAccessControl),我遇到了很多问题,多谢老哥的指点:http://blog.csdn.net/lovingprince/article/details/6935465;好了废话不多说,先上文档译文:
zk使用ACL去控制对它的节点(zk的data树中的data节点:znode)的访问权限。ACL的实现与UNIX文件访问权限类似:它使用权限块来(控制)允许或拒绝在某节点上或其应用的域上的操作。与标准的UNIX权限不同,一个zk节点不会受限于3个标准的‘域’:user(文件本身),group,world(其他)。zk并没有节点所有者概念。相反,一个ACL会指定ids及与其关联的权限。
同样需要注意的是:一个ACL只从属一个指定的znode。尤其是它(ACL)并不会应用到子节点。例如,'/app'只可以被ip:172.16.16.1读取,且'/app/'的状态是局可读,任何客户端都可以读取'/app/'状态;ACL不可以递归。
zk支持可扩展的认证scheme(认证提供者)。Id格式指定为'scheme:id',其中scheme是id字符串指定的一种认证策略。例如,'ip:172.16.16.1'就是地址为'172.16.16.1'的主机的id字符串。
/** Id数据结构 public class Id implements Record { private String scheme; private String id; } **/
/** ACL数据结构 public class ACL implements Record { private int perms;//permission 权限 private org.apache.zookeeper.data.Id id;//即Id } **/
expression的格式特定于scheme,就是说scheme不同,expression的格式就不同,例如,权限对 (ip:19.22.0.0/16,READ)赋予IP地址以'19.22'开头的任意客户端READ权限。
ACL Permissions
zk支持下列权限:
·CREATE:创建子节点
·READ:获取节点数据getData()和其子节点列表getChildren()
·WRITE:节点数据赋值setData()
·DELETE:删除子节点
·ADMIN:设置权限
CREATE and DELETE权限从WIRTE权限中分离出来,为了更出色的细粒度访问控制。CREATE和DELETE的具体情况如下:
你希望A可以在zk节点上进行setData()操作,但不能创建或删除子节点;
原生的ACL scheme
zk拥有下列原生的 scheme:
·world 只有一个简单的id,'anyone',代表了所有客户端
·auth 不使用任何id,代表任意以认证的用户
·digest username:password
·ip addr/bits
·x509
学习文档只是一个初步的了解,找到zk权限认证的源码:
认证提供者接口:AuthenticationProvider
package org.apache.zookeeper.server.auth; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.server.ServerCnxn; /** * 实现这个接口,添加新的认证schemes到zk中 * 源码中提供了3种策略(scheme),即:ip、digest、sasl */ public interface AuthenticationProvider { /** * @return the scheme of this provider. * 返回此认证提供者scheme */ String getScheme(); /** * This method is called when a client passes authentication data for this * scheme. The authData is directly from the authentication packet. The * implementor may attach new ids to the authInfo field of cnxn or may use * cnxn to send packets back to the client. * * @param cnxn * the cnxn that received the authentication information. * @param authData * the authentication data received. * @return TODO */ KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte authData[]); /** * @return true if the id can be matched by the expression. */ boolean matches(String id, String aclExpr); /** * @return true if this provider identifies creators. */ boolean isAuthenticated(); /** * @return true if id is well formed. * 校验id字符串 */ boolean isValid(String id); }认证提供者注册中心:ProviderRegistry
package org.apache.zookeeper.server.auth; import java.util.Enumeration; import java.util.HashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.apache.zookeeper.server.ZooKeeperServer; /** * 认证服务提供者注册中心 * */ public class ProviderRegistry { private static final Logger LOG = LoggerFactory.getLogger(ProviderRegistry.class); private static boolean initialized = false;//初始化标识 true时,标识以完成初始化 private static HashMap<String, AuthenticationProvider> authenticationProviders = new HashMap<String, AuthenticationProvider>();//认证服务提供者容器 public static void initialize() { synchronized (ProviderRegistry.class) { if (initialized) return; IPAuthenticationProvider ipp = new IPAuthenticationProvider();//ip认证 DigestAuthenticationProvider digp = new DigestAuthenticationProvider();//文摘认证 authenticationProviders.put(ipp.getScheme(), ipp);//默认 authenticationProviders.put(digp.getScheme(), digp);//默认 //将自定义的认证服务提供者配置到properties文件,key格式为 以"zookeeper.authProvider."开头,value为 类名 Enumeration<Object> en = System.getProperties().keys(); while (en.hasMoreElements()) { String k = (String) en.nextElement(); if (k.startsWith("zookeeper.authProvider.")) { String className = System.getProperty(k); try { Class<?> c = ZooKeeperServer.class.getClassLoader() .loadClass(className);//zk服务加载自定义认证服务提供者 AuthenticationProvider ap = (AuthenticationProvider) c .newInstance(); authenticationProviders.put(ap.getScheme(), ap); } catch (Exception e) { LOG.warn("Problems loading " + className,e); } } } initialized = true; } } public static AuthenticationProvider getProvider(String scheme) { if(!initialized) initialize(); return authenticationProviders.get(scheme); } public static String listProviders() { StringBuilder sb = new StringBuilder(); for(String s: authenticationProviders.keySet()) { sb.append(s + " "); } return sb.toString(); } }
IP认证提供者:IpAuthentictaionProvider
package org.apache.zookeeper.server.auth; import org.apache.zookeeper.KeeperException; import org.apache.zookeeper.data.Id; import org.apache.zookeeper.server.ServerCnxn; /** * * IP认证服务提供者 */ public class IPAuthenticationProvider implements AuthenticationProvider { public String getScheme() { return "ip"; } public KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte[] authData) { String id = cnxn.getRemoteSocketAddress().getAddress().getHostAddress();//IP地址 cnxn.addAuthInfo(new Id(getScheme(), id));// /** public abstract class ServerCnxn implements Stats, Watcher { //... protected ArrayList<Id> authInfo = new ArrayList<Id>(); //... public void addAuthInfo(Id id) { if (authInfo.contains(id) == false) { authInfo.add(id); } } //... } **/ //OK为枚举;KeeperException抽象类继承了java.lang.Exception return KeeperException.Code.OK;//返回一个枚举,3.10版本之后,KeeperException.Code(实现了CodeDeprecated)取代CodeDeprecated } // This is a bit weird but we need to return the address and the number of // bytes (to distinguish between IPv4 and IPv6 private byte[] addr2Bytes(String addr) { byte b[] = v4addr2Bytes(addr); // TODO Write the v6addr2Bytes return b; } private byte[] v4addr2Bytes(String addr) { String parts[] = addr.split("\\.", -1); if (parts.length != 4) { return null; } byte b[] = new byte[4]; for (int i = 0; i < 4; i++) { try { int v = Integer.parseInt(parts[i]); if (v >= 0 && v <= 255) { b[i] = (byte) v; } else { return null; } } catch (NumberFormatException e) { return null; } } return b; } private void mask(byte b[], int bits) { int start = bits / 8; int startMask = (1 << (8 - (bits % 8))) - 1; startMask = ~startMask; while (start < b.length) { b[start] &= startMask; startMask = 0; start++; } } public boolean matches(String id, String aclExpr) { String parts[] = aclExpr.split("/", 2); byte aclAddr[] = addr2Bytes(parts[0]); if (aclAddr == null) { return false; } int bits = aclAddr.length * 8; if (parts.length == 2) { try { bits = Integer.parseInt(parts[1]); if (bits < 0 || bits > aclAddr.length * 8) { return false; } } catch (NumberFormatException e) { return false; } } mask(aclAddr, bits); byte remoteAddr[] = addr2Bytes(id); if (remoteAddr == null) { return false; } mask(remoteAddr, bits); for (int i = 0; i < remoteAddr.length; i++) { if (remoteAddr[i] != aclAddr[i]) { return false; } } return true; } public boolean isAuthenticated() { return false; } public boolean isValid(String id) { return addr2Bytes(id) != null; } }由此看出,认证服务的源码集中在org\apache\zookeeper\server\auth包下,包含了3种认证策略,1个认证服务接口,和一个注册中心完成认证策略的初始化,但并未找到zk中ACL的认证机制,就是说认证策略有了,但是它又是如何实现的呢?保持好奇心,在以后的学习中寻找答案吧。