老大让我看cdh4的安全机制的实现,说实话,没有太多的思路。然后老大说,安全认证不通过肯定会抛异常,然后我就从异常入手了。
目测 AccessControlException比较像,然后看到他有个子类:AuthorizationException。这个受众更小,更容易做为切入点,好,就这个了。
public void authorize(UserGroupInformation user,
Class<?> protocol,
Configuration conf,
InetAddress addr
) throws AuthorizationException {
AccessControlList acl = protocolToAcl.get(protocol);
if (acl == null) {
throw new AuthorizationException("Protocol " + protocol +
" is not known.");
}
// get client principal key to verify (if available)
KerberosInfo krbInfo = SecurityUtil.getKerberosInfo(protocol, conf);
String clientPrincipal = null;
if (krbInfo != null) {
String clientKey = krbInfo.clientPrincipal();
if (clientKey != null && !clientKey.equals("")) {
try {
clientPrincipal = SecurityUtil.getServerPrincipal(
conf.get(clientKey), addr);
} catch (IOException e) {
throw (AuthorizationException) new AuthorizationException(
"Can't figure out Kerberos principal name for connection from "
+ addr + " for user=" + user + " protocol=" + protocol)
.initCause(e);
}
}
}
if((clientPrincipal != null && !clientPrincipal.equals(user.getUserName())) ||
!acl.isUserAllowed(user)) {
AUDITLOG.warn(AUTHZ_FAILED_FOR + user + " for protocol=" + protocol
+ ", expected client Kerberos principal is " + clientPrincipal);
throw new AuthorizationException("User " + user +
" is not authorized for protocol " + protocol +
", expected client Kerberos principal is " + clientPrincipal);
}
AUDITLOG.info(AUTHZ_SUCCESSFUL_FOR + user + " for protocol="+protocol);
}
这里是会抛出AuthorizationException异常的。
我比较关注kerberos的部分:clientPrincipal.equals(user.getUserName()
clientPrincipal = SecurityUtil.getServerPrincipal(
conf.get(clientKey), addr);
KerberosInfo krbInfo = SecurityUtil.getKerberosInfo(protocol, conf);
关键是clientKey,但是clientKey需要krbInfo。所以krbInfo就是最关键的地方了。
for(SecurityInfo provider: securityInfoProviders) {
KerberosInfo result = provider.getKerberosInfo(protocol, conf);
那这里是的securityInfoProviders是怎么来的呢:
private static ServiceLoader<SecurityInfo> securityInfoProviders =
ServiceLoader.load(SecurityInfo.class);
这里使用了ServiceLoader,我们看到hadoop-commom的META-INF\services文件夹,有个叫org.apache.hadoop.security.SecurityInfo的文件名,内容是
org.apache.hadoop.security.AnnotatedSecurityInfo
。
public class AnnotatedSecurityInfo extends SecurityInfo {
@Override
public KerberosInfo getKerberosInfo(Class<?> protocol, Configuration conf) {
return protocol.getAnnotation(KerberosInfo.class);
}
意思就是返回protocol对应类的注解KerberosInfo 就可以了。
protocol = getProtocolClass(protocolName, getConf());
这个类似根据protocolName反射得到的。
可以看到protocolName是从数据流中读取的,
processOneRpc(data.array());
这里的data就是protocolName的源头了。
现在能拿到clientKey,再回过头来。
clientPrincipal = SecurityUtil.getServerPrincipal(
conf.get(clientKey), addr);
这里就与conf扯上关系了,conf的各个key和value都是系统初始化进去的。
clientPrincipal 只是conf对应的value替换掉主机名而已。看到替换规则,这些value应该是以[/@]来分隔的,但是我看了conf的初始化过程,没有找见有这样的value.