java 安全

JAAS 参考指南

2019-05-29  本文已影响0人  spraysss

术语中英对照

缩写 全拼 中文解释
JAAS Java Authentication and Authorization Service Java身份验证和授权服务
PAM Pluggable Authentication Module 可插拔身份验证模块

概览

JAAS 代表Java身份验证和授权服务,它被用于两个目的:

JAAS实现了标准可插拔身份验证模块(PAM)框架的Java版本

传统的java提供了基于codesource-based 访问控制(访问控制基于代码的来源和谁签名了该代码)。但是,它缺乏根据谁运行代码来附加强制访问控制的能力。JAAS提供了一个框架,通过这种支持来增强Java安全体系结构。

JAAS身份验证是以可插入的方式执行的。这允许应用程序独立于底层身份验证技术,可以在应用程序下插入新的或更新的身份验证技术,而不需要修改应用程序本身。应用程序通过实例化LoginContext对象来启用身份验证过程,而LoginContext对象又引用ConfigurationLoginModule,用于执行身份验证。典型的LoginModules可能会提示输入和验证用户名和密码

执行代码的用户或服务经过身份验证后,JAAS授权组件将与核心Java SE访问控制模型一起工作,以保护对敏感资源的访问。访问控制决策既基于执行代码的CodeSource,也基于运行代码的用户或服务,后者由Subject对象表示,如果身份验证成功,则使用相关Principals和credentials 的LoginModule更新Subject。

核心类和接口

与jaas相关的核心类和接口可以分为三类:Common, Authentication, and Authorization.

Common 类

公共类是由JAAS身份验证和授权组件共享的类。关键的JAAS类是javax.security.auth.Subject,它表示单个实体(如人)的相关信息的分组。它包含实体的Principals、公共credentials和私有credentials

Subject

要授权访问资源,应用程序首先需要对请求的源进行身份验证。JAAS框架定义了表示请求源的术语subject。主体可以是任何实体,例如人或服务。一旦对主题进行了身份验证,则使用javax.security.auth.Subject由关联的标识或Principals填充。一个subject可能有多个principal。例如,一个人可能有一个name Principal(“John Doe”)和一个SSN Principal(“123-45-6789”)

subject还可以拥有与安全性相关的属性,这些属性称为credentials凭据,敏感的credentials比如私钥使用Set<Object> privCredentials表示,共享非敏感credentials比如公钥使用Set<Object> pubCredentials表示。访问和修改不同的凭据集需要不同的权限(如下所述):

subject是使用这些构造函数创建的:

 public Subject();

    public Subject(boolean readOnly, Set principals,
                   Set pubCredentials, Set privCredentials);

第一个构造函数创建了一个principalcredential为空(非空)的subject。第二个构造函数使用指定的principalscredentials创建subject。它还有一个布尔参数,可用于使subject为只读。在只读subject中,principalscredentials是不可变的。

应用程序编写者不必实例化subject。如果应用程序实例化了一个LoginContext,而没有将Subject传递给LoginContext构造函数,那么LoginContext实例化了一个新的空Subject

如果没有在实例化Subject使其处于只读状态,则可以通过调用以下方法将其设置为只读:

 public boolean isReadOnly();

要获取与subject相关的principals,有两种方法:

 public Set getPrincipals();
 public Set getPrincipals(Class c);

第一个方法返回subject中的所有principals,而第二个方法只返回指定类c的实例或类c的子类的实例化的principals。如果subject没有任何关联的principals,则返回一个空集合。

获取subject的公有凭证和私有凭证的方式和获取主体的方式类似

 public Set getPublicCredentials();
 public Set getPublicCredentials(Class c);
 public Set getPrivateCredentials();
 public Set getPrivateCredentials(Class c);

要修改或操作subjectprincipalspubCredentialsprivCredentials,调用者使用java.util.Set中定义的方法。下面的示例演示了这一点:

    Subject subject;
    Principal principal;
    Object credential;

    . . .

    // add a Principal and credential to the Subject
    subject.getPrincipals().add(principal);
    subject.getPublicCredentials().add(credential);

主题可以与AccessControlContext关联,下面的方法返回与指定AccessControlContext关联的subject,如果没有与指定AccessControlContext关联的subject,则返回null

    public static Subject getSubject(final AccessControlContext acc);

作为特定Subject执行操作的doAs方法
可以调用下列静态方法来执行作为特定Subject的操作:

    public static Object
        doAs(final Subject subject,
             final java.security.PrivilegedAction action);

    public static Object
        doAs(final Subject subject,
             final java.security.PrivilegedExceptionAction action)
             throws java.security.PrivilegedActionException;

这两个方法首先将指定的Subject与当前线程的AccessControlContext关联,然后执行操作。这实现了将动作作为Subject运行的效果。第一个方法可以抛出运行时异常,但正常执行时它必须从其action参数的run方法返回一个对象,第二个方法的行为类似,只是它可以从PrivilegedExceptionAction的 run方法中抛出一个已检查的异常

Subject.doAs Example
假设名为“Bob”的人已经通过LoginContext(请参阅LoginContext)进行了身份验证,因此,Subject 中填充了com.ibm.security.Principal类的Principal。这个Principal的名字叫BOB,还假设已经安装了SecurityManager,并且访问控制策略中存在以下内容:

    // grant "BOB" permission to read the file "foo.txt"
    grant Principal com.ibm.security.Principal "BOB" {
        permission java.io.FilePermission "foo.txt", "read";
    };

下面是示例应用程序代码

    class ExampleAction implements java.security.PrivilegedAction {
        public Object run() {
            java.io.File f = new java.io.File("foo.txt");

            // the following call invokes a security check
            if (f.exists()) {
                System.out.println("File foo.txt exists");
            }
            return null;
        }
    }

    public class Example1 {
        public static void main(String[] args) {

            // Authenticate the subject, "BOB".
            // This process is described in the
            // LoginContext class.

            Subject bob;
            // Set bob to the Subject created during the
            // authentication process

            // perform "ExampleAction" as "BOB"
            Subject.doAs(bob, new ExampleAction());
        }
    }

在执行过程中,ExampleAction调用f.exists()时将遇到安全检查。但是,由于ExampleAction作为“BOB”运行,并且策略(上面)将必要的文件权限授予“BOB”,因此ExampleAction将通过安全检查。如果策略中的grant语句被更改(例如,添加不正确的CodeBase 或将主体更改为“MOE”),则会抛出SecurityException。

doAsPrivileged methods

    public static Object doAsPrivileged(
        final Subject subject,
        final java.security.PrivilegedAction action,
        final java.security.AccessControlContext acc);

    public static Object doAsPrivileged(
        final Subject subject,
        final java.security.PrivilegedExceptionAction action,
        final java.security.AccessControlContext acc)
        throws java.security.PrivilegedActionException;

doAs与doAsPrivileged

doasprivilege方法的行为与doAs方法完全相同,只是它需要通过传入一个AccessControlContext而不是将提供的subject与当前线程的AccessControlContext关联。通过这种方式,可以通过与当前上下文不同的AccessControlContext来控制操作。

AccessControlContext包含自实例化AccessControlContext以来执行的所有代码的信息,包括代码位置和策略授予代码的权限。为了使访问控制检查成功,策略必须为AccessControlContext引用的每个代码项授予所需的权限。

如果提供给doasprivilegeAccessControlContextnull,则操作不受单独的AccessControlContext的限制。比如在服务器环境中。服务器可以对多个传入请求进行身份验证,并为每个请求执行单独的doAs操作。要启动每个doAs操作,并且不受当前服务器AccessControlContext的限制,服务器可以调用doAsPrivileged并传入nullAccessControlContext

Principals

正如前面提到的,旦对主题进行了身份验证,则使用javax.security.auth.Subject由关联的标识或Principals填充。一个subject可能有多个principal。例如,一个人可能有一个name Principal(“John Doe”)和一个SSN Principal(“123-45-6789”)。主体必须实现java.security.Principaljava.io.Serializable的接口

Credentials

除了关联的主体外,主题还可以拥有与安全性相关的属性,这些属性称为凭据。凭据可能包含用于向新服务验证主题的信息。这些凭证包括密码、Kerberos票据和公钥证书。凭据还可能包含仅使主体能够执行某些活动的数据。例如,加密密钥表示使主体能够签名或加密数据的凭据。公共凭证类和私有凭证类不是核心JAAS类库的一部分。因此,任何类都可以表示凭证

:公共凭证类和私有凭证类不是核心JAAS类库的一部分。然而,开发人员可以选择让他们的凭据类实现两个与凭据相关的接口:RefreshableDestroyable

Refreshable

javax.security.auth.Refreshable接口提供了凭据刷新自身的功能。例如,具有特定时间限制生命周期的凭据可以实现此接口,以允许调用者在其有效刷新刷新。该接口有两种抽象方法:

boolean isCurrent();

此方法确定凭据是当前的还是有效的

 void refresh() throws RefreshFailedException;

此方法更新或扩展凭据的有效性

Destroyable

javax.security.auth.Destroyable接口提供了销毁凭据内内容的功能。该接口有两个抽象方法

  boolean isDestroyed();

确定凭据是否已被销毁

  void destroy() throws DestroyFailedException;

销毁并清除与此凭据关联的信息。对该凭证上的某些方法的后续调用将导致抛出IllegalStateException

Authentication 类和接口

认证是验证subject身份的过程,必须以安全的方式执行;否则,犯罪者可能会冒充他人来访问系统。身份验证通常涉及到subject展示某种形式的证据来证明其身份。这些证据可能是只有受试者可能知道或拥有的信息(例如密码或指纹),也可能是只有受试者能够生成的信息(例如使用私钥签名的数据)。
要对subject(用户或服务)进行身份验证,需要执行以下步骤:

  1. 应用程序实例化LoginContext
  2. LoginContext查询一个Configuration,以加载为该应用程序配置的所有LoginModules
  3. 应用程序调用LoginContextlogin 方法
  4. login方法调用所有加载的LoginModules 。每个LoginModule都尝试对subject进行身份验证,成功后,LoginModules将相关principalscredentials与表示正在验证的主题的Subject对象关联。
  5. LoginContext将身份验证状态返回给应用程序
  6. 如果身份验证成功,应用程序将从LoginContext获取主题。
LoginContext

javax.security.auth.login.LoginContext类提供了用于对subject进行身份验证的基本方法,并提供了一种独立于底层身份验证技术开发应用程序的方法。LoginContext查询配置,以确定为特定应用程序配置的身份验证服务(或LoginModule)。因此,可以在应用程序下插入不同的LoginModules,而不需要对应用程序本身进行任何修改。
LoginContext提供了四个构造函数可供选择:

    public LoginContext(String name) throws LoginException;

    public LoginContext(String name, Subject subject) throws LoginException;

    public LoginContext(String name, CallbackHandler callbackHandler)
           throws LoginException

    public LoginContext(String name, Subject subject,
           CallbackHandler callbackHandler) throws LoginException

所有构造函数都共享一个公共参数:nameLoginContext使用这个参数作为登录配置的索引,以确定为实例化LoginContext的应用程序配置了哪些LoginModule
不接受Subject作为输入参数的构造函数实例化一个新Subject。
实际身份验证通过调用以下方法进行:

 public void login() throws LoginException;

当调用login时,将调用所有配置的loginmodule来执行身份验证。如果认证成功,可以使用以下方法检索Subject(现在可以保存主体、公共凭证和私有凭证):

 public Subject getSubject();

要注销Subject并删除其经过身份验证的主体和凭据,提供以下方法:

public void logout() throws LoginException;

下面的代码示例演示了验证和注销主题所需的调用:

 // let the LoginContext instantiate a new Subject
    LoginContext lc = new LoginContext("entryFoo");
    try {
        // authenticate the Subject
        lc.login();
        System.out.println("authentication successful");

        // get the authenticated Subject
        Subject subject = lc.getSubject();

        ...

        // all finished -- logout
        lc.logout();
    } catch (LoginException le) {
        System.err.println("authentication unsuccessful: " +
            le.getMessage());
    }
LoginModule

LoginModule接口使开发人员能够实现可以在应用程序中插入的各种身份验证技术。例如,一种类型的LoginModule可以执行基于用户名/密码的身份验证形式。其他LoginModule可以与硬件设备(如智能卡或生物识别设备)来进行认证

CallbackHandler

在某些情况下,LoginModule必须与用户通信才能获得身份验证信息javax.security.auth.callback.CallbackHandler就是用于此目的。应用程序实现CallbackHandler接口并将其传递给LoginContext,后者将其直接转发给底层LoginModule
LoginModule使用CallbackHandler收集用户的输入(如密码或智能卡密码)或向用户提供信息(如状态信息),通过允许应用程序指定CallbackHandler,底层LoginModule可以实现不同的应用程序与用户交互方式。例如,GUI应用程序的CallbackHandler实现可能会显示一个窗口来请求用户输入。另一方面,非gui工具的CallbackHandler实现可能只是直接从命令行提示用户输入。

CallbackHandler是一个接口,有一个方法可以实现:

 void handle(Callback[] callbacks)
         throws java.io.IOException, UnsupportedCallbackException;

LoginModuleCallbackHandlerhandle方法传递一系列适当的回调函数,例如用户名的NameCallback和密码的PasswordCallback, CallbackHandler执行请求的用户交互并在回调函数中设置适当的值。例如,要处理NameCallback, CallbackHandler可能会提示输入名称,然后获取输入name,并调用NameCallbacksetName方法来存储name

Callback

javax.security.auth.callback包包含回调接口和几个实现。LoginModules可以将一个回调数组直接传递给CallbackHandlerhandle方法。

Authorization 类
上一篇 下一篇

猜你喜欢

热点阅读