使用JAAS以及GSSAPI登录Kerberos

 

    最近为了解决本地发布测试代码到服务器的问题,试用了下JAAS(Java Authencation and Authorization Service,Java授权与验证服务)和GSSAPI来完成Kerberos登录验证。完成了身份验证以后,就可以使用ant标准任务SCP来完成发布操作了。

    关于Kerberos验证机制请参考:一个不错的Kerberos机制讲解blog(虽然头像有点,但不要以貌取文)

    基于Kerberos的验证机制,需要两步来完成登录服务器身份验证:

    1、通过JAAS完成客户端与KDC服务器的验证工作,并获得登录服务器的ticket

    2、使用GSSAPI登录服务器

注:以下代码能够正常执行的前提是,执行的机器上已经正常配置了Kerberos登录的文件,可以参考Kerberos登录软件的配置方式,确保使用Kerberos的登录软件可以正常登录服务器之后再尝试以下代码。下面的代码只是用于辅助自动登录,并不包含具体Kerberos登录的配置



1、使用JAAS登录Kerberos账号

    关于JAAS的详细信息请参考:Sun的Jaas和Gssapi指南 。

    我使用的代码与指南中的基本相同,所以具体内容的详细解释请参考:Jaas验证样例

    使用JAAS登录Kerberos登录的步骤如下:

  1. JAAS服务中已经提供了Kerberos登录的实现,需要在启动程序时添加配置文件信息来指定登录的模式为Kerberos,例如:-Djava.security.auth.login.config=/data/jaas.conf,或者配置System属性也可以。
  2. jaas.conf的内容很简单,只是指定了登录模式为Kerberos:
    KerberosLogin 
    {
            com.sun.security.auth.module.Krb5LoginModule required debug=true;
    };
     
  3. 使用JAAS服务需要调用LoginContext(javax.security.auth.login包)来尝试登录,代码如下:
            LoginContext lc = null;
    
            try {
                lc = new LoginContext
    ("KerberosLogin
    ", new TextCallbackHandler());   //“KerberosLogin”这个名称必须与配置文件中的名称相同
            } catch (LoginException le) {
                System.err.println("Cannot create LoginContext. " + le.getMessage());
                System.exit(-1);
            } catch (SecurityException se) {
                System.err.println("Cannot create LoginContext. " + se.getMessage());
                System.exit(-1);
            }
    
            try {
                // attempt authentication
                lc.login();
    
            } catch (LoginException le) {
                System.err.println("Authentication failed:");
                System.err.println("  " + le.getMessage());
                System.exit(-1);
            }
     
  4. 如果正确的配置了Kerberos的登录信息,则以上代码如果正常运行完就表示客户端与KDC的通讯已经成功完成。

 

2、使用GSSAPI完成SSH的身份验证

 

     完成了客户端与KDC的通讯后,剩下的任务就是登录服务器了。以下的代码必须在成功完成步骤1之后进行,具体步骤为:

  1. 在subject的权限下进行登录。完成了步骤一之后,我们可以从login context中获得身份验证的一个subject对象,这个对象中包含了当前用户的权限,接下来的登录服务器操作必须在这个subject的权限范围内完成,如
    Subject.doAsPrivileged(lc.getSubject()
    ,
    
                    new java.security.PrivilegedExceptionAction() {
    
                        @Override
                        public Object run() throws Exception {
                             //登录操作
                        }
                     }
    }   
       
  2. 构建GSS context对象。这个context对象包含了用于登录服务器的ticket以及客户端的相关信息。
    try {
                GSSManager manager = GSSManager.getInstance();
                //Kerberos的oid
                Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
    
                GSSName serverName = manager.createName("host/" + hostName, null);
    
                GssContext context = manager.createContext(serverName, krb5Mechanism, null, GSSContext.DEFAULT_LIFETIME);
                context.requestMutualAuth(true); // Mutual authentication
                context.requestConf(true); // Will use confidentiality later
                context.requestInteg(true); // Will use integrity later
            } catch (GSSException e) {
                // catch Exception
            }
     
  3. 拥有了GSS context对象之后,就可以进行SSH的身份验证,完成了身份验证之后就可以像普通的SCP一样进行操作了。下面的代码只包含了SSH中使用GSSAPI进行验证的过程,不包含SSH的建立,交换key以及后续的sftp操作。
    /*
      *   使用GSSAPI进行SSH验证的基本顺序是
      *   1、询问user-auth服务
      *   2、获得确认后传输验证的方式
      *   3、进行Kerberos的验证操作
      */
    
    1、询问user-auth服务
    请求数据: 5                            byte     
                    “ssh-userauth”      String
    响应标志    6
    
    2、请求身份验证方法为Kerberos 
         50                         byte  
         username             string      登录服务器的名称,不是登录Kerberos的名称
         "ssh-connection"  string
         "gssapi-with-mic" string
          1                           int          表示只有一种类型的oid在后面
          new Oid("1.2.840.113554.1.2.2").getDER()   Kerberos的oid的DER编码
    响应标志     60  表示支持Kerberos登录
    
    3、进行Kerberos验证,这里需要使用刚才构建的GSS context对象
            byte[] token = new byte[0];
    
            while (!context.isEstablished()) {
                try {
                    token = context.init(token, 0, token.length);
                } catch (JSchException e) {             
                    e.printStackTrace();
                    return;
                }
    
                if (token != null) {
                    // 会写数据
                    //  61        byte
                    //  token   string
                }
    
                if (!context.isEstablished()) {
                    // 读取返回信息,忽略错误和失败的情况
                    // 以下代码代表读取返回信息,并设置给token,用与继续context的初始化参数
                    buf.getInt();
                    buf.getByte();
                    buf.getByte();
                    token = buf.getString();
                }
            }
    
     
  4. 注:这段代码省略了关于SSH通讯数据的解析和输出部分,具体关于SSH的通讯格式请参数wikipedia的SSH页面 。

     

     

    你可能感兴趣的:(Java)