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

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

扫描二维码关注公众号,回复: 437911 查看本文章

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


猜你喜欢

转载自blog.csdn.net/soongp/article/details/62043688