zookeeper实战与源码分析----ACL访问控制

来源:互联网 发布:巴基斯坦海关数据 编辑:程序博客网 时间:2024/06/06 00:41

最近工作一般忙吧,利用工作之余学习学习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;}**/


当一个客户端连接zk时会进行认证,通过客户端连接,zk会把客户端的所有Id联系到一起。当一个客户端尝试访问某节点时,通过节点的ACL(可以多个,但是一个ACL只能属于一个znode)去校验这些ids(即:Id)。ACL由一对字符串组成(scheme:expression,perms)。


/**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的认证机制,就是说认证策略有了,但是它又是如何实现的呢?保持好奇心,在以后的学习中寻找答案吧。


0 0