缘起
一直被这个三头恶犬欺负,今天我们分析一下它们的实现机制。
一提到kerberos,就会想起来一些概念,keytab, principal, tgt, ticket, KDC, JAAS, subject, credential, UGI, GSS等,概念难懂,代码也很难理解。
kerberos本身的概念这里就不分析了,自行看介绍。主要分析下java代码层面的东西。
初识UGI
首先,这里有个文章不错,大体分析了一下UGI这个对象的一些重要方法。
https://steveloughran.gitbooks.io/kerberos_and_hadoop/content/sections/ugi.html
这里需要对照UGI源码分析。
下面的是一个辅助理解UGI中group如何获得的文章,当然只是辅助,因为只是片面的理解
http://www.cnblogs.com/zwCHAN/p/4686054.html
如果看了代码,就会知道里面很多LoginContext, Subject, LoginModule, Credential之类的对象,其实都是来自于java的JAAS框架,要理清楚头绪,就要看看JAAS庐山真面目
初识JAAS
下面给出2个JAAS代码框架的例子
例子1
http://blog.csdn.net/xiao_jun_0820/article/details/39375819
Java的安全机制
详细介绍请参考JAAS:灵活的Java安全机制
简单来说,用户首先使用LoginContext的接口进行登录验证。LoginContext可以配置使用不同的验证协议。验证通过后,用户得到一个subject,里面包含凭证,公私钥等。之后,在涉及到需要进行权限认证的地方(例如,资源访问,外部链接校验,协议访问等),使用doAs函数()代替直接执行。
这样,java的权限认证就和用户的业务逻辑分离了。
//一段典型的代码如下
LoginContext lc = new LoginContext("MyExample");
try {
lc.login();
} catch (LoginException) {
// Authentication failed.
}
// Authentication successful, we can now continue.
// We can use the returned Subject if we like.
Subject sub = lc.getSubject();
Subject.doAs(sub, new MyPrivilegedAction());
例子2
http://www.codeweblog.com/%E5%9F%BA%E4%BA%8Ekerberos%E5%AE%9E%E7%8E%B0jaas%E7%99%BB%E5%BD%95/
package security;
import java.io.File;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
public class KerberosClient {
public static void main(String[] args) {
String loginModuleName = "KerberosLogin";
Subject subject = new Subject();
Krb5Configuration conf = new Krb5Configuration();
try {
String sep = File.separator;
System.out.println("KerberosClient.main():"
+ System.getProperty("java.home") + sep + "lib" + sep
+ "security" + sep + "java.security");
LoginContext context = new LoginContext("myKerberosLogin", subject,
null, conf);
context.login();
System.out.println(context.getSubject().getPrincipals());
} catch (LoginException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
package security;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import javax.security.auth.login.Configuration;
public class Krb5Configuration extends Configuration{
private AppConfigurationEntry[] entry = new AppConfigurationEntry[1];
Map paramMap = new HashMap();
private AppConfigurationEntry krb5LoginModule = new AppConfigurationEntry("com.sun.security.auth.module.Krb5LoginModule", LoginModuleControlFlag.REQUIRED, paramMap);
@Override
public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
if(entry[0] == null){
paramMap.put("debug", "true");
//1.enter the username and passsword
//paramMap.put("storeKey", "true");
//paramMap.put("doNotPrompt", "false");
//2.use keytab file
paramMap.put("doNotPrompt", "true");
paramMap.put("useKeyTab", "true");
paramMap.put("keyTab", "/hadoop-data/etc/hadoop/nn.service.keytab");
paramMap.put("principal", "nn/admin@psy.com");
paramMap.put("useTicketCache", "true");
paramMap.put("ticketCache", "/hadoop-data/etc/hadoop/keytab_cache");
entry[0] = krb5LoginModule;
}
return entry;
}
}
JAAS 框架图
jaas对象介绍
http://blog.sina.com.cn/s/blog_84feb25101015but.html
http://docs.huihoo.com/java/j2ee/jaas.html
LoginContext
javax/security/auth/login/LoginContext.java
/**
* Perform the authentication.
*
* <p> This method invokes the <code>login</code> method for each
* LoginModule configured for the <i>name</i> specified to the
* <code>LoginContext</code> constructor, as determined by the login
* <code>Configuration</code>. Each <code>LoginModule</code>
* then performs its respective type of authentication
* (username/password, smart card pin verification, etc.).
*
* <p> This method completes a 2-phase authentication process by
* calling each configured LoginModule's <code>commit</code> method
* if the overall authentication succeeded (the relevant REQUIRED,
* REQUISITE, SUFFICIENT, and OPTIONAL LoginModules succeeded),
* or by calling each configured LoginModule's <code>abort</code> method
* if the overall authentication failed. If authentication succeeded,
* each successful LoginModule's <code>commit</code> method associates
* the relevant Principals and Credentials with the <code>Subject</code>.
* If authentication failed, each LoginModule's <code>abort</code> method
* removes/destroys any previously stored state.
*
* <p> If the <code>commit</code> phase of the authentication process
* fails, then the overall authentication fails and this method
* invokes the <code>abort</code> method for each configured
* <code>LoginModule</code>.
*
* <p> If the <code>abort</code> phase
* fails for any reason, then this method propagates the
* original exception thrown either during the <code>login</code> phase
* or the <code>commit</code> phase. In either case, the overall
* authentication fails.
*
* <p> In the case where multiple LoginModules fail,
* this method propagates the exception raised by the first
* <code>LoginModule</code> which failed.
*
* <p> Note that if this method enters the <code>abort</code> phase
* (either the <code>login</code> or <code>commit</code> phase failed),
* this method invokes all LoginModules configured for the
* application regardless of their respective <code>Configuration</code>
* flag parameters. Essentially this means that <code>Requisite</code>
* and <code>Sufficient</code> semantics are ignored during the
* <code>abort</code> phase. This guarantees that proper cleanup
* and state restoration can take place.
*
* <p>
*
* @exception LoginException if the authentication fails.
*/
public void login() throws LoginException {
loginSucceeded = false;
if (subject == null) {
subject = new Subject();
}
try {
// module invoked in doPrivileged
invokePriv(LOGIN_METHOD);
invokePriv(COMMIT_METHOD);
loginSucceeded = true;
} catch (LoginException le) {
try {
invokePriv(ABORT_METHOD);
} catch (LoginException le2) {
throw le;
}
throw le;
}
}
这里看一下注释,login的过程采用的是2PC,也就是两阶段提交
2阶段提交
http://www.hollischuang.com/archives/681
http://blog.csdn.net/whycold/article/details/47702133
http://www.hollischuang.com/archives/1580
2阶段提交3阶段提交,然后就是paxos协议,这是分布式系统中的常见保证原子性的机制
http://www.hollischuang.com/archives/693 paxos小解
说起了分布式系统,就顺便看看分布式锁吧
分布式锁的几种实现:http://www.hollischuang.com/archives/1716
http://www.hollischuang.com博客很不错
源码分析
UerGroupInformation.java
/**
* Information about the logged in user.
*/
private static UserGroupInformation loginUser = null;
private static String keytabPrincipal = null;
private static String keytabFile = null;
private final Subject subject;
// All non-static fields must be read-only caches that come from the subject.
private final User user;
private final boolean isKeytab;
private final boolean isKrbTkt;
private static String OS_LOGIN_MODULE_NAME;
private static Class< extends Principal> OS_PRINCIPAL_CLASS;
这里
下面的文章也很不错,相当于做了一个大致的梳理
https://my.oschina.net/drl/blog/676084