大数据的安全管理 -- JAAS 认证代码

1. JaaS代码

在前面已经说明了JAAS的使用,在这一节,主要是解析JAAS在认证时的代码流程,我们可以看到其认证过程只有两行代码:

LoginContext lc = new LoginContext("JaaSSampleTest", new TextCallbackHandler());
lc.login();

我们可以把它分为二个部分:上下文设置, 登录

1.1 上下文的设置

LoginContext lc = new LoginContext("JaaSSampleTest", new TextCallbackHandler());

程序首先会通过jaas.conf获取JaaSSampleTest所对应的类:

JaaSSampleTest {
 com.sun.security.auth.module.Krb5LoginModule required;
};

从jaas.conf配置文件,可以知道所应的类为Krb5LoginModule ,查看Krb5LoginModule 的类:

   public void initialize(Subject subject,
                           CallbackHandler callbackHandler,
                           Map<String, ?> sharedState,
                           Map<String, ?> options) {

        this.subject = subject;
        this.callbackHandler = callbackHandler;
        ... ... 

即Krb5LoginModule 初始化的时候,将TextCallbackHandler的句柄设进去。后面会调用其handle()方法

1.2 登录过程

当程序创建了LoginContext的时候,就会调用

            lc.login()

通过LoginContext.login()的代码,知道,它的主要动作在:

            invokePriv(LOGIN_METHOD);
            invokePriv(COMMIT_METHOD);

我们查看首先会调用的Krb5LoginModule::login()

    public boolean login() throws LoginException {

        int len;
        //首先校验参数信息
        validateConfiguration();
        ... ... 

        if (tryFirstPass) {
            try {
                //此函数会根据需求获取对应的TGT信息。这其中有两种情况:
                //1. 用户执行完kinit之后,会保存对应的TGT信息到对应的文件中。这个文件位置由krb5.conf中的default_ccache_name进行定义 。 程序会直接读取这个文件信息而获取TGT信息
                //2. 第二种情况,如果用户不希望直接通过配置文件获取,就可以通过直接发送消息的方式获取对应的TGT信息。 
                //其相应的代码在:KrbAsReqBuilder::action()中
                attemptAuthentication(true);
                 ... ... 
    }

其实login()的主要动作就是执行attemptAuthentication()这个函数。而这个函数中的主要动作就是获取TGT信息。而在attemptAuthentication(),会回调到CallbackHandler::handle()函数,处理用户名与密码。

下面再看一下commit()的作用, 对于commit的作用,程序的注释中有如下一段话:

    /*
     * Let us add the Krb5 Creds to the Subject's
     * private credentials. The credentials are of type
     * KerberosKey or KerberosTicket
     */

从它的代码中也可以看到,它将之前的login()中的cred设置到subject中。

1.3 小结

再次梳理一下整个jaas的认证过程,可以分为如下几个步骤:

  1. 初始化LoginContext的上下文,这其中就包含两部分:

    • 从jaas.conf中读取实际对应的context Moudle,
    • 将CallbackHandler设置到Module, 而这个CallbackHanlder::handler()的函数中最重要的就是
      name与password的处理
  2. 调用login()方法, 而这个login()方法又可以分为三个步骤:

    • 回调Moudle的login()方法
    • 在Module方法中会回调CallbackHandler的handler方法
    • 最后就是回调Moudle的commit方法

2 密码非交互登录

我们知道KDC认证有两种方法:keytab方式与密码方式。Krb5LoginModule 提供了keytab直接登录的方式,但是无法使用它完成密码登录。下面给出将用户名、密码非交互登录的方式

public class JaasTestPassword {
    
    
    public static void main(String[] args) {
        String username =  args[0];
        String passwd =  args[1];
        Krb5Configuration conf = new Krb5Configuration();

        try {
            LoginContext lc = new LoginContext("JaaSSampleTest",
                     new Subject(),
                     JaasTestPassword.createJaasCallbackHandler(username,
                             passwd), conf);
            lc.login();
            Subject sub = lc.getSubject();
        } catch (LoginException le) {
            System.err.println("Authentication failed:");
            System.exit(-1);
        }
        System.out.println("Authentication succeeded!");
    }

    public static CallbackHandler createJaasCallbackHandler(
            final String principal, final String password) {
        return new CallbackHandler() {
            public void handle(Callback[] callbacks) throws IOException,
                    UnsupportedCallbackException {
                for (Callback callback : callbacks) {
                    if (callback instanceof NameCallback) {
                        NameCallback nameCallback = (NameCallback) callback;
                        nameCallback.setName(principal);
                    } else if (callback instanceof PasswordCallback) {
                        PasswordCallback passwordCallback = (PasswordCallback) callback;
                        passwordCallback.setPassword(password.toCharArray());
                    } else {
                        throw new UnsupportedCallbackException(callback,
                                "Unsupported callback: "
                                        + callback.getClass()
                                                .getCanonicalName());
                    }
                }
            }
        };
    }
}

配置文件的代码:

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) {
        // TODO Auto-generated method stub
        if (entry[0] == null) {
            paramMap.put("debug", "false"); //是否打印debug日志
            paramMap.put("storeKey", "true");
            paramMap.put("doNotPrompt", "false"); //直接使用密码登录,设置为false
            paramMap.put("useTicketCache", "false"); //是否使用ticket
            entry[0] = krb5LoginModule;
        }
        return entry;
    }
}

密码登录的参数

3. 小结

  1. 整个JaaS登录,我们可以把它分为三个部分:LogContext, Config配置信息、CallBackHandler部分。其中Config配置给定登录方式(keytab还是cache)等信息,而CallBackHandler则是通过handler()方法将KDC认证需要用到的信息(如princal信息,密码或keytab)设置到相应的配置中。从而完成认证
  2. 在整个login()是从KDC获取TGT,而commit()则将这个TGT真充到Subject中,在这里根本不涉及到客户端与服务端的交互。

猜你喜欢

转载自blog.csdn.net/eyoulc123/article/details/78782164