UserGroupInformation中的几种user介绍
"HDFS", "MapReduce", "HBase", "Hive", "Oozie"这几个组件均使用了hadoop common中UserGroupInformation类来进行用户/组信息的处理。在这个类里面有几种用户的概念时常困扰我们,现在我们来认识一下具体有那几种用户。
首先是loginUser,在loginUserFromKeytab函数中展示的就是kerberos认证的代码:
public synchronized static void loginUserFromKeytab(String user, String path) throws IOException { if (!isSecurityEnabled()) return; keytabFile = path; keytabPrincipal = user; Subject subject = new Subject(); LoginContext login; long start = 0; try { login = newLoginContext(HadoopConfiguration.KEYTAB_KERBEROS_CONFIG_NAME, subject); start = System.currentTimeMillis(); login.login(); metrics.loginSuccess.add(System.currentTimeMillis() - start); loginUser = new UserGroupInformation(subject); loginUser.setLogin(login); loginUser.setAuthenticationMethod(AuthenticationMethod.KERBEROS); } catch (LoginException le) { if (start > 0) { metrics.loginFailure.add(System.currentTimeMillis() - start); } throw new IOException("Login failure for " + user + " from keytab " + path, le); } LOG.info("Login successful for user " + keytabPrincipal + " using keytab file " + keytabFile); } |
整个认证过程就是为了生成一个login(存储这认证需要的所有信息的上下文)和subject(认证后的票据信息等均在此变量中),并且将login和subject和一个UserGroupInformation实例关联,这个实例就是loginUser,即loginUser就代表了一个通过kerberos认证的用户。
因此对于UserGroupInformation的getLoginUser()方法,就是获取当前的loginUser实例,如果loginUser还未存在,即进行上述认证过程新生成一个loginUser。
那么对于UserGroupInformation的getCurrentUser()方法与上面的getLoginUser()差别有在哪呢?
public synchronized static UserGroupInformation getCurrentUser() throws IOException { AccessControlContext context = AccessController.getContext(); Subject subject = Subject.getSubject(context); if (subject == null || subject.getPrincipals(User.class).isEmpty()) { return getLoginUser(); } else { return new UserGroupInformation(subject); } } |
如果通过当前的上下文(一直以来都觉得上下文是个很抽象的东西,我这里据个例子,比如代码为subjectuserA.doas {xxx},那么在xxx里面获取上下文里的subject就是subjectuserA了)已经能够获取现成的subject,那么就用当前的subject新生成一个UserGroupInformation实例返回,如果当前上下文未获取到现成的subject则调用getLoginUser()方法,新认证一个用户然后构造UserGroupInformation实例返回。
接下来UserGroupInformation的getRealUser()是最复杂的一个了,RealUser只有在Proxy认证时才会被使用到。
public static UserGroupInformation createProxyUser(String user, UserGroupInformation realUser) { if (user == null || "".equals(user)) { 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)); principals.add(new RealUser(realUser)); UserGroupInformation result =new UserGroupInformation(subject); result.setAuthenticationMethod(AuthenticationMethod.PROXY); return result; } |
我们来看看org.apache.hadoop.ipc.Client的认证代码
private synchronized void setupIOstreams() throws InterruptedException {
......
while (true) { setupConnection(); InputStream inStream = NetUtils.getInputStream(socket); OutputStream outStream = NetUtils.getOutputStream(socket); writeConnectionHeader(outStream); if (useSasl) { final InputStream in2 = inStream; final OutputStream out2 = outStream; UserGroupInformation ticket = remoteId.getTicket(); if (authMethod == AuthMethod.KERBEROS) { if (ticket.getRealUser() != null) { ticket = ticket.getRealUser(); } } boolean continueSasl = false; try { continueSasl = ticket .doAs(new PrivilegedExceptionAction<Boolean>() { @Override public Boolean run() throws IOException { return setupSaslConnection(in2, out2); } }); } ...... } |
如果当前建立RPC连接的subject存在RealUser,则RPC客户端代码会使用RealUser的票据信息来进行到服务端的认证,那么服务端识别到该用户到底是谁呢?
来看看认证服务端org.apache.hadoop.ipc.Server
private void processConnectionContext(byte[] buf) throws IOException {
......
{ // user is authenticated user.setAuthenticationMethod(authMethod.authenticationMethod); //Now we check if this is a proxy user case. If the protocol user is //different from the 'user', it is a proxy user scenario. However, //this is not allowed if user authenticated with DIGEST. if ((protocolUser != null) && (!protocolUser.getUserName().equals(user.getUserName()))) { if (authMethod == AuthMethod.DIGEST) { // Not allowed to doAs if token authentication is used throw new AccessControlException("Authenticated user (" + user + ") doesn't match what the client claims to be (" + protocolUser + ")"); } else { // Effective user can be different from authenticated user // for simple auth or kerberos auth // The user is the real user. Now we create a proxy user UserGroupInformation realUser = user; user = UserGroupInformation.createProxyUser(protocolUser .getUserName(), realUser); // Now the user is a proxy user, set Authentication method Proxy. user.setAuthenticationMethod(AuthenticationMethod.PROXY); } } }
......
} |
如果客户端指明用户与服务端认证用户名不一致时,(必须是kerberos认证方式,如果是digest则不可能存在这种情况)服务端会自己根据当前用户信息创建一个ProxyUser来支撑后续业务处理。所以这里最终业务用户还是之前客户端代码中蓝色部分new User(user),只是这个用户自己没有票据而是借助了new RealUser(realUser)的票据来进行认证过程。
- 点赞
- 收藏
- 关注作者
评论(0)