在非安全模式下,访问hdfs文件系统的客户端代码如下:
package ntci.hadoop.hdfs.test; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.*; import org.apache.hadoop.security.UserGroupInformation; import java.io.*; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; /** * Created by ctt on 2017/4/9. */ public class HDFSClient { public static void main(String[] args) throws IOException { try { Configuration conf = new Configuration(); conf.set("fs.defaultFS","hdfs://hadoop-m-21:8020"); conf.set("fs.hdfs.impl",org.apache.hadoop.hdfs.DistributedFileSystem.class.getName());//如果在pom.xml中配置了如下依赖,可省 // <groupId>org.apache.hadoop</groupId> // <artifactId>hadoop-hdfs</artifactId> // <version>2.6.0</version> UserGroupInformation.setConfiguration(conf);//初始化用户组信息,并设置用户权限信息。即对UGI设置静态配置,特别是设置安全认证机制和组查找服务。 FileSystem fs = FileSystem.get(conf); FileStatus[] fsStatus = fs.listStatus(new Path("/")); for (int i = 0;i < fsStatus.length;i++){ System.out.println(fsStatus[i].getPath().toString()); } } catch (IOException e) { e.printStackTrace(); } } }使用域名:HADOOP.COM。
在root用户下,使用kadmin.local命令,本地操作KDC数据库:kadmin.local -q “addprinc test”,回车,输入两次test用户的密码:test。 这样,获取到test用户的 principal:test@HADOOP.COM,默认的主机名为:ctt-m-48。
(1)输入kinit test回车,输入其密码test,回车,得到test用户的TGT,或者(2)kinit -k -t test.keytab test@HADOOP.COM(kinit obtains and caches an initial ticket-granting ticket for principal),票据存放在TGT cache:/tmp/krb5cc_*本地缓存中(其中*表示的是用户的ID)。
kinit [-V] [-l lifetime] [-s start_time] [-r renewable_life] [-p | -P] [-f | -F] [-a] [-A] [-C] [-E] [-v] [-R] [-k [-t keytab_file]] [-c cache_name] [-n] [-S service_name] [-I input_ccache] [-T armor_ccache] [-X attribute[=value]] [principal]
可以通过使用命令:klist -e查看test用户的TGT,如下所示,可以看到TGT的创建日期,有效期,属主以及类型等信息。
在root用户下使用kadmin.local -q “xst -norandkey -k test.keytab test@HADOOP.COM”命令。同时将生成的test.keytab文件放到/etc/security/keytabs文件夹下。
(1)Ticket-Granting-Ticket(TGT):这张ticket需要用户手动获取,这张ticket是用户获取Service Ticket的凭证。用户需要提供Kerberos principal和密码或者keytab通过Kerberos认证才能够获取TGT,获取后TGT会缓存在用户的客户端(/tmp/krb5cc_*)。
(2)Service Ticket:访问集群中各个服务的凭证。Kerberos会自动根据客户端缓存的TGT来向用户发放Service Ticket,用户手动获取了TGT后 无需 自己获取Service Ticket。
(3)要通过Kerberos的认证需要提供principal及其对应的密码。密码可以手动输入,也可以存放在一个keytab文件中。“Keytab”是“key table”的简写,它用于存放一个或多个principal的密码。进行Kerberos认证时,一个用户可以提供principal和密码,或者principal和keytab文件。如果使用principal和keytab文件认证,那么Kerberos会去keytab文件中读取principal密码。
HDFS初始化是指在使用HDFS提供的API之前,需要做的必要工作。过程为:加载HDFS服务配置文件,并进行kerberos安全认证,认证通过后再实例化Filesystem,之后使用HDFS的API。
Application:JAAS使用者使用Java语言编写的应用。
LoginContext:可以看做JAAS为上层Java应用提供的住入口,上层应用通过调用LoginContext提供的方法,与底层的认证支撑体系对接。
xxxLoginModule:JAAS将底层不同的实际账号认证服务进行抽象,比如RDBMS抽象成RdbmsLoginModule等等。有了LogingModule的抽象能力,让JAAS的使用者可以根据自己的需求选择实际的账号存储体系及账号认证的服务架构,只需要以相对应的LoginModule与上层(LoginContext)相对接上就可以了。
(1)创建LoginContext实例lc。 (2)调用lc的login方法实现认证。(认证失败时会抛出LoginException)
LoginModule的主要方法: (1) initialize () :创建LoginModule实例时会被构造函数调用。 (2)login ():进行验证,调用LoginContext的login方法之后,逐个调用所关联的每个LoginModule的login方法。 (3)commit ():当LgoninContext对象接受所有LoginModule对象传回的结果后将调用该方法。该方法将Principal对象和凭证赋给Subject对象。 (4)abort () 当任何一个LoginModule对象验证失败时都会调用该方法。此时没有任何Principal对象或凭证关联到Subject对象上。 (5)logout () 删除与Subject对象关联的Principal对象和凭证。
对于一个LoginModule来说,在用户调用LoginContext的login方法之后,实际经历的主要方法调用流程就是:
(6)成功时调用序列:initialize->login->commit (7)失败时调用序列:initialize->login->abort
在认证的过程当中,当用户进行认证登录之后,其信息需要存储于某个对象当中,以备之后的操作基于该对象提供的方法进行,对象自身携带的信息可以充分说明对象是经过某用户成功认证之后生成的。
Java中,该对象为Subject类的实例,Subject中所包含信息: (1)Principal(可以简单类比为好理解的username,不严谨) (2)Credentials(public/private) (可简单类比为password,不严谨)
Subject的中关于对象属性的这些信息,在LoginModule的commit方法中会被赋上正确的值。
Hadoop认证的实现类为org.apache.hadoop.security.UserGroupInformation。
从上面对JAAS的简述很容易想象,Hadoop为了定义自己的认证机制,实际就是实现了一个自己的LoginModule,在Java应用调用LoginContext的login方法之后,触发Hadoop自定义的LoginModule中的逻辑。
UserGroupInformation的内部类HadoopConfiguration 通过在HadoopConfiguration()中定义要使用的LoginModule,在Java应用调用LoginContext的login方法之后,触发Hadoop自定义的LoginModule中的逻辑。如上所述,通过kerberos登录的用户使用的AppConfigurationEntry为KEYTAB_KERBEROS_LOGIN, HADOOP_LOGIN两个,其中KEYTAB_KERBEROS_LOGIN调用的登录模块是Krb5LoginModule,而HADOOP_LOGIN调用的登录模块是HadoopLoginModule。接下来,就来看看Krb5LoginModule这一登录模块所做的事情。
按照如上的代码所示,在加载过相应的loginEntry也即LoginModule之后,按照成功时调用序列:initialize->login->commit。 (1)初始化该登录模块(Krb5LoginModule): (2)生成登陆上下文对象定义为login,通过login.login(),登陆。 (3)调用commit()方法负责把Principal(test@HADOOP.COM)添加到Subject中。 注:其中ticket cache中存放的主要内容是:current ticket lifetime、session key、krbtgt/service principal。而所有的票据缓存在/tmp/krb5cc_*文件中。
(4)kerberos中相应的credential的组成 (5)生成ticket的函数:
public static KerberosTicket credsToTicket(Credentials var0) { EncryptionKey var1 = var0.getSessionKey(); return new KerberosTicket(var0.getEncoded(), new KerberosPrincipal(var0.getClient().getName()), new KerberosPrincipal(var0.getServer().getName(), 2), var1.getBytes(), var1.getEType(), var0.getFlags(), var0.getAuthTime(), var0.getStartTime(), var0.getEndTime(), var0.getRenewTill(), var0.getClientAddresses()); }