最近工作一般忙吧,利用工作之余学习学习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的认证机制,就是说认证策略有了,但是它又是如何实现的呢?保持好奇心,在以后的学习中寻找答案吧。