跳到内容

JAAS 备忘单

简介 - 什么是 JAAS 认证

验证用户或另一个系统身份的过程就是认证。

JAAS 作为一个认证框架,负责管理经过认证的用户从登录到注销的身份和凭证。

JAAS 认证生命周期

  1. 创建 LoginContext
  2. 从配置文件中读取一个或多个 LoginModule 以进行初始化。
  3. 为每个 LoginModule 调用 LoginContext.initialize() 以进行初始化。
  4. 为每个 LoginModule 调用 LoginContext.login()
  5. 如果登录成功则调用 LoginContext.commit(),否则调用 LoginContext.abort()

配置文件

JAAS 配置文件为每个可用于登录应用程序的 LoginModule 包含一个 LoginModule 节。

JAAS 配置文件中的一个节

Branches
{
    USNavy.AppLoginModule required
    debug=true
    succeeded=true;
}

注意分号的位置,它们终止了 LoginModule 条目和节。

“required” 一词表示 LoginContextlogin() 方法在用户登录时必须成功。LoginModule 特定的值 debugsucceeded 会传递给 LoginModule

它们由 LoginModule 定义,其用法在 LoginModule 内部进行管理。请注意,选项使用键值对(例如 debug="true")进行配置,键和值之间应以 = 符号分隔。

Main.java (客户端)

  • 执行语法
Java –Djava.security.auth.login.config==packageName/packageName.config
        packageName.Main Stanza1

Where:
    packageName is the directory containing the config file.
    packageName.config specifies the config file in the Java package, packageName.
    packageName.Main specifies Main.java in the Java package, packageName.
    Stanza1 is the name of the stanza Main() should read from the config file.
  • 执行时,第一个命令行参数是配置文件的节。该节指定了要使用的 LoginModule。第二个参数是 CallbackHandler
  • 使用传递给 Main.java 的参数创建一个新的 LoginContext
    • loginContext = new LoginContext (args[0], new AppCallbackHandler());
  • 调用 LoginContext.Login 模块
    • loginContext.login();
  • succeeded 选项中的值由 loginContext.login() 返回。
  • 如果登录成功,则创建了一个主体 (Subject)。

LoginModule.java

LoginModule 必须具有以下认证方法

  • initialize()
  • login()
  • commit()
  • abort()
  • logout()

initialize()

Main() 中,当 LoginContext 从配置文件中读取正确的节后,LoginContext 会实例化该节中指定的 LoginModule

  • initialize() 方法签名
    • Public void initialize (Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options)
  • 以上参数应按如下方式保存
    • this.subject = subject;
    • this.callbackHandler = callbackHandler;
    • this.sharedState = sharedState;
    • this.options = options;
  • initialize() 方法的作用
    • login() 成功后,构建一个 Subject 类的对象。
    • 设置 CallbackHandler,它与用户交互以收集登录信息。
    • 如果一个 LoginContext 指定了两个或更多 LoginModule(这是合法的),它们可以通过 sharedState 映射共享信息。
    • 将调试 (debug) 和成功 (succeeded) 等状态信息保存在 options 映射中。

login()

捕获用户提供的登录信息。以下代码片段声明了一个包含两个回调对象的数组,当它们传递给 callbackHandler.java 程序中的 callbackHandler.handle 方法时,将加载用户交互式提供的用户名和密码。

NameCallback nameCB = new NameCallback("Username");
PasswordCallback passwordCB = new PasswordCallback ("Password", false);
Callback[] callbacks = new Callback[] { nameCB, passwordCB };
callbackHandler.handle (callbacks);
  • 认证用户
  • 从回调对象中检索用户提供的信息
    • String ID = nameCallback.getName ();
    • char[] tempPW = passwordCallback.getPassword ();
  • nametempPW 与存储在 LDAP 等存储库中的值进行比较。
  • 设置变量 succeeded 的值并返回到 Main()

commit()

login() 期间用户凭证成功验证后,JAAS 认证框架会根据需要将凭证与主体关联起来。

凭证有两种类型:公共私有

  • 公共凭证包括公钥。
  • 私有凭证包括密码和公钥。

主体(即主体除了登录名之外的其他身份),例如员工编号或用户组中的成员 ID,会被添加到主体中。

下面是一个 commit() 方法的示例,首先,对于经过认证的用户所属的每个组,组名都会作为主体添加到主体中。然后,主体的用户名会添加到其公共凭证中。

设置并向主体添加任何主体(Principal)和公共凭证的代码片段

public boolean commit() {
    If (userAuthenticated) {
        Set groups = UserService.findGroups (username);
        for (Iterator itr = groups.iterator (); itr.hasNext (); {
            String groupName = (String) itr.next ();
            UserGroupPrincipal group = new UserGroupPrincipal (GroupName);
            subject.getPrincipals ().add (group);
        }
        UsernameCredential cred = new UsernameCredential (username);
        subject.getPublicCredentials().add (cred);
    }
}

abort()

当认证不成功时,会调用 abort() 方法。在 abort() 方法退出 LoginModule 之前,应注意重置状态,包括用户名和密码输入字段。

logout()

当调用 LoginContext.logout 时,释放用户的主体(principals)和凭证

public boolean logout() {
    if (!subject.isReadOnly()) {
        Set principals = subject.getPrincipals(UserGroupPrincipal.class);
        subject.getPrincipals().removeAll(principals);
        Set creds = subject.getPublicCredentials(UsernameCredential.class);
        subject.getPublicCredentials().removeAll(creds);
        return true;
    } else {
        return false;
    }
}

CallbackHandler.java

callbackHandler 位于与任何单个 LoginModule 分离的源 (.java) 文件中,以便它可以服务于具有不同回调对象的多种 LoginModule

  • 创建 CallbackHandler 类的实例,并且只有一个方法 handle()
  • 一个服务于需要用户名和密码才能登录的 LoginModule 的 CallbackHandler
public void handle(Callback[] callbacks) {
    for (int i = 0; i < callbacks.length; i++) {
        Callback callback = callbacks[i];
        if (callback instanceof NameCallback) {
            NameCallback nameCallBack = (NameCallback) callback;
            nameCallBack.setName(username);
    }  else if (callback instanceof PasswordCallback) {
            PasswordCallback passwordCallBack = (PasswordCallback) callback;
            passwordCallBack.setPassword(password.toCharArray());
        }
    }
}

披露

所附 JAAS 备忘单中的所有代码均逐字复制自此免费来源