下面大概了解下面Java的认证相关框架
JAAS 认证和授权框架,只要负责用户的认证和权限。
SASL client 和 server之间认证的框架
GSS 是sasl的一个provider,也就是实现了sasl框架
参考JAAS/GSS-API/SASL/Kerberos简介 | NoSQL漫谈
网上关于high level介绍的还比较多,可以搜索一些,但是要真正理解UserGroupInfomration的功能,还是需要研究 UserGroupInformaiton在 hadoop/hive等大数据系统中如何使用的。
介绍UserGroupInformation 之前,应该了解一下 JaaS框架里 Subject这个概念,简单理解就是代表了一个用户。当通过JAAS登录成功之后,会产生一个subject, 里面包含了一组 Principal, 和其他凭证信息,不如如果是Kerbos认证的话,就会有一个Kerbos身份的principal, 以及把tgt等Credentials的信息放到 privCredentials中。
CASE ONE, 客户端和服务端建立连接
当client 跟Server端建立连接的时候,一般会使用
UserGroupInfomation.doAs(action {
建立连接的代码
})
doAs就会使用UserGroupInformation当前subject执行
这样在建立连接的过程中,就是用了登录后的subject 来建立链接,因为subject包含了kerbos的Principal, 并且拥有合法的Credentials, sasl client和 sasl server在 建立连接的过程中就会使用到。 真实的底层使用的是Gss的实现。
doAs 里面的实现逻辑
@InterfaceAudience.Public
@InterfaceStability.Evolving
public <T> T doAs(PrivilegedAction<T> action) {
if (LOG.isDebugEnabled()) {
LOG.debug("PrivilegedAction [as: {}][action: {}]", this, action,
new Exception());
}
//使用当前subject执行action,也就是建立连接的时候使用登录过的subject
return Subject.doAs(subject, action);
}
当然,如果没有使用UserGroupInformation.doAs的时候,
建立sasl连接的时候使用的就是当前安全上线文里的subject, 用户已经使用Jaas登录过的,登录之后,subject的信息已经存在AccessConect中了,所以也会使用登录后的subject。
CASE TWO 在服务端执行ACTION
连接到服务端之后,一般服务端有两个UGI,一个是服务本身的UGI,比如NN 一般是hdfs, 或者MetaStore, 一般是hive。 hdfs或者 hive用户是服务里的超级用户,客户端一般创建连接之后,执行的操作的用户是 client的用户,这时候就会用到UserGroupInformation里面 proxyUser的概念。
proxyUser顾名思义,就是代理一个用户执行操作。切记,被代理的用户在服务端是没有通过JAAS 认证过的用户,只是给一个用户名而已。 不过这个用户在客户端已经通过JAAS认证过了。
创建代理用户的代码如下:
/**
* Create a proxy user using username of the effective user and the ugi of the
* real user.
* @param user
* @param realUser
* @return proxyUser ugi
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public static UserGroupInformation createProxyUser(String user,
UserGroupInformation realUser) {
if (user == null || user.isEmpty()) {
throw new IllegalArgumentException("Null user");
}
if (realUser == null) {
throw new IllegalArgumentException("Null real user");
}
Subject subject = new Subject();
Set<Principal> principals = subject.getPrincipals();
principals.add(new User(user, AuthenticationMethod.PROXY, null));
principals.add(new RealUser(realUser));
return new UserGroupInformation(subject);
}
代理用户的UserGroupInformation里的subject是创建出来的,里面设置了一个 User的principal, 以及一个 RealUser的principal. 这个RealUser的principal是拥有kerbos认证过的,拥有kerbos秘钥。
代理用户的 subject里,没有任何秘钥信息。
如果用户被代理了,一般在服务端执行action的时候都会使用下面格式
UserGroupInformation.doAs
这样在需要权限认证的地方,就获取到的是代理用户的用户名。
切记,不能用代理用户创建 sasl的connection, 因为没有凭证,会失败的。
获取当前用户
UserGroupInformation.getCurrentUser
/**
* Return the current user, including any doAs in the current stack.
* @return the current user
* @throws IOException if login fails
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public static UserGroupInformation getCurrentUser() throws IOException {
ensureInitialized();
AccessControlContext context = AccessController.getContext();
Subject subject = Subject.getSubject(context);
if (subject == null || subject.getPrincipals(User.class).isEmpty()) {
return getLoginUser();
} else {
return new UserGroupInformation(subject);
}
}
从安全上下文获取当前的subject, 如果没有,就获取 getLoginUser, 这时候就触发登录操作。
注意1 里面有一个判断条件是 subject.getPrincipals(User.class).isEnpty()
也就是只有subject里有User这种principals才算是登录过的。
注意2,第一行执行了ensuerInitialized(),这个方法很重要,初始化了hadoop的安全环境.
登录
/**
* Get the currently logged in user. If no explicit login has occurred,
* the user will automatically be logged in with either kerberos credentials
* if available, or as the local OS user, based on security settings.
* @return the logged in user
* @throws IOException if login fails
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public static UserGroupInformation getLoginUser() throws IOException {
ensureInitialized();
UserGroupInformation loginUser = loginUserRef.get();
// a potential race condition exists only for the initial creation of
// the login user. there's no need to penalize all subsequent calls
// with sy