Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

范围

Geode can be configured to authenticate peer system members, clients, and remote gateways. Geode also provides for authorization of cache operations on a server from clients. This allows to block unauthenticated access to a Geode Distributed System, or block cache operations as per user defined policies. When the peer system members are configured for authentication, then the Geode system is required to use the locator service for discovery.

The Geode authentication and authorization sub-system is implemented as a framework allowing applications to plug-in external providers such as LDAP, Kerberos, etc. We find that most enterprise customers typically have a common infrastructure such as single sign-on or centralized authentication systems they need to integrate with. Geode provides sample implementations based on LDAP and PKCS along with source code that users can adapt to suit their requirements or roll their own.

It is also possible to use SSL (Secure Sockets Layer) for "on-the-wire" confidentiality in Geode.

Note that authentication and entitlement management is provided for each VM that joins a Geode Distributed System, or for client/gateway processes. Each member node can only represent a single 'principal' or 'user'. Authentication at a connection level (support for multiple users in a single process) will be added in a future release.

We first walk through the authentication interfaces provided as part of security framework with simple implementations. Then configuring Geode to use those implementations is described. The sample implementations provided with Geode are described next. In the latter part, more advanced implementations like integration with a Kerberos realm, object-level authorization are provided.

Interfaces for authentication callbacks

AuthInitialize

The first requirement is obtaining credentials for a client/peer which is catered to by the AuthInitialize interface. This is required for a client or peer or gateway that needs to connect to a Geode Distributed System which has security enabled (more on enabling security in section "Configuration of authentication callbacks for peers").

Code Block
languagejava
public interface AuthInitialize extends CacheCallback {
  public void init(LogWriter systemLogger, LogWriter securityLogger)
      throws AuthenticationFailedException;
  public Properties getCredentials(Properties securityProps,
      DistributedMember server, boolean isPeer)
      throws AuthenticationFailedException;
}

 

The first method "init" in the interface is to initialize the callback with the given LogWriters. Usually a callback will use the securityLogger for logging purpose which is a special logger for security that will mark log lines with a "security-" prefix and the logs can also be configured to go to a separate log file. A sample implementation is below:

可以配置来认证Peer系统成员、客户端、以及远程网关。 Geode 还为客户端对服务器的缓存操作提供授权。 这样就阻止了未经身份验证和用户定义的缓存操作策略访问Geode分布式系统。当Peer成员配置了身份认证,那么需要使用Locator服务来发现 Geode 分布式系统。

这个Geode认证授权子系统是作为一个框架被实现,允许应用打补丁给外部认证服务如LDAP、Kerberos等,我们发现,大多数企业客户通常都有自己的安全基础设施需要整合,如单点登录或集中认证系统。 Geode提供了基于LDAP和PKCS的示例实现,并且提供源代码,用户可以根据自身的要求进行实现或者二次开发。

在使用Geode时也可以使用SSL(安全套接字层)保证数据传输的机密性。

注意:认证和授权管理是提供给加入Geode的分布式系统的每个VM,或者客户端、网关进程。每个成员节点只能代表单一的“principal”或“user”。在连接层面(支持一个进程中多个用户)的身份验证将在未来的版本中添加。

我们首先通过实现简单的认证接口作为安全框架的一部分。然后通过上面描述的实现来配置Geode。示例实现在下面提供了一些描述。在更后面的一部分,展示与Kerberos领域的整合,提供对象级验证更高级的实现。

认证回调接口

认证初始化

第一个需求是Client/Peer实现了Authinitialize接口。这个是需要连接到一个已启用安全的Geode分布式系统的一个Client、Peer或网关的需求(更多的获取安全相关请见“配置Peer之间的认证回调”章节)。

Code Block
languagejava
public interface AuthInitialize extends CacheCallback {
  public void init(LogWriter systemLogger, LogWriter securityLogger)
      throws AuthenticationFailedException;
  public Properties getCredentials(Properties securityProps,
      DistributedMember server, boolean isPeer)
      throws AuthenticationFailedException;
}

 

接口里的第一个方法“init”用给定的LogWriters初始化回调。通常一个回调将使用securitylogger为记录的目的,这是一种特殊的记录器,它将标记每行日志以"security-"作为前缀,并且也可以配置到一个单独的日志文件。下面是示例实现:

 

Code Block
languagejava
public class SampleAuthInit implements AuthInitialize {
  private LogWriter logger;
  public void init(LogWriter systemLogger, LogWriter securityLogger)
      throws AuthenticationFailedException {
    this.logger = securityLogger;
  }

一个虚拟机获得凭证的方法通过"getCredentials" 方法实现。

参数securityProps 包含Geode特性中的"security-"前缀。 服务器的参数是用于远程服务器的主机、端口和其他成员信息(对于客户端或网关的情况)或Locator/Peer(对于Peer成员的情况下),这将对该成员进行身份验证。当远程成员是Peer或Locator时参数isPeer为true,而当远程成员是一个服务器时参数isPeer为false。这是有用的,如果一个成员既是一个分布式系统中的一个Peer,并且是另一个分布式系统的客户端/网关,这时候该成员需要为这两个系统准备不同的凭证。

一个简单的用户名/密码的实现可以要求“security-username”属性被设置为用户的名称,“security-password”属性被设置为密码(后者通常是程序中设置的),所有需要做的事是传回来的这两个属性如下:

 

Code Block
languagejava
public Properties getCredentials(Properties securityProps, DistributedMember server, boolean isPeer)
 throws AuthenticationFailedException {
 Properties credentials = new Properties();
 String userName = securityProps.getProperty("security-username");
 credentials.setProperty("security-username", userName);
 String passwd = securityProps.getProperty("security-password");
 credentials.setProperty("security-password", passwd);
 logger.info("SampleAuthInit: successfully obtained credentials for user " + userName);
 return credentials;}

在上面代码之上加一些检查,我们的示例实现像下面这样:

 

Code Block
languagejava
package com.gemstone.samples;
import java.util.Properties;
import com.gemstone.gemfire.LogWriter;
import com.gemstone.gemfire.distributed.DistributedMember;
import com.gemstone.gemfire.security.AuthInitialize;
import com.gemstone.gemfire.security.AuthenticationFailedException;
public class SampleAuthInit implements AuthInitialize {
  private LogWriter logger;
  public static final String USER_NAME = "security-username";
  public static final String PASSWORD = "security-password";
  public static AuthInitialize create() {
    return new SampleAuthInit();
  }
Code Block
languagejava
public class SampleAuthInit implements AuthInitialize {
  private LogWriter logger;
  public void init(LogWriter systemLogger, LogWriter securityLogger)
      throws AuthenticationFailedException {
    this.logger = securityLogger;
  }

The work of obtaining credentials for a VM is done by the "getCredentials" method.

The securityProps argument contains the set of Geode properties containing the prefix "security-". The server argument is for the host, port and other membership information of remote server (for the case of clients or gateways) or locator/peer (for the case of peer members) which will authenticate this member. The isPeer argument is passed as true when the remote member is a peer or locator, while it is false when the remote member is a server. This is useful if a member is both a peer in a Distributed System and client/gateway for another Distributed System and the member needs to use different credentials for the two.

A simple user/password implementation may expect "security-username" property to be set for the name of user, and "security-password" property to be set for the password (latter would normally be set programmatically) so all that is needed to be done is pass back the two properties as below:

Code Block
languagejava
public Properties getCredentials(Properties securityProps, DistributedMember server, boolean isPeer)
 throws AuthenticationFailedException {
 Properties credentials = new Properties();
 String userName
  public Properties getCredentials(Properties securityProps,
      DistributedMember server, boolean isPeer)
      throws AuthenticationFailedException {
    Properties credentials = new Properties();
    String userName = securityProps.getProperty(USER_NAME);
    if (userName == null) {
      throw new AuthenticationFailedException(
          "SampleAuthInit: user name property [" + USER_NAME + "] not set.");
    }
    credentials.setProperty(USER_NAME, userName);
    String passwd = securityProps.getProperty("security-username"PASSWORD);
 credentials.setProperty("security-username", userName);
 String passwd = securityProps.getProperty("security-password");
 credentials.setProperty("security-password"   if (passwd == null) {
      throw new AuthenticationFailedException(
          "SampleAuthInit: password property [" + PASSWORD + "] not set.");
    }
    credentials.setProperty(PASSWORD, passwd);
    logger.info("SampleAuthInit: successfully obtained credentials for user "
 + userName);
       + userName);
    return credentials;
  }
  public void close() {
  }
}  

Adding a few checks in the above code, our sample implementation looks like below:

 

上面的实现代码中的“close”方法来自于CacheCallback 接口,是继承AuthInitialize 得到的。 静态方法”create”用来创建一个用来注册回调的接口的实例。

认证器

其次是认证接口,要求实现服务器对一个新的客户端或者Peer的认证。这个回调提供加盟(连接)成员的凭证,该凭证是从AuthInitialize类中getCredentials 方法的属性获得。

Code Block
languagejava
public interface Authenticator extends CacheCallback {
 public void init(Properties securityProps, LogWriter systemLogger,
 LogWriter securityLogger) throws AuthenticationFailedException;
 public Principal authenticate(Properties props, DistributedMember member)
 throws AuthenticationFailedException;
}

 

 这有几个方法(除了“close”方法的继于CacheCallback 接口)。第一个“init”是用来执行任何初始化回调提供了LogWriters用于记录。参数 securityProps 提供了所有这些成员的Geode的特性,以前缀“security-”开始。对于我们的示例实现我们将再次使用 securityLogger 提供。

Code Block
languagejava
 public class SampleAuthenticator implements Authenticator
Code Block
languagejava
package com.gemstone.samples;
import java.util.Properties;
import com.gemstone.gemfire.LogWriter;
import com.gemstone.gemfire.distributed.DistributedMember;
import com.gemstone.gemfire.security.AuthInitialize;
import com.gemstone.gemfire.security.AuthenticationFailedException;
public class SampleAuthInit implements AuthInitialize {
  private LogWriter logger;
  public staticvoid final String USER_NAME = "security-username";init(Properties securityProps, LogWriter systemLogger,
  public static final String PASSWORDLogWriter = "security-password";
  public static AuthInitialize create() securityLogger) throws AuthenticationFailedException {
    returnthis.logger new= SampleAuthInit()securityLogger;
  }
  public void init(LogWriter systemLogger, LogWriter securityLogger)
      throws AuthenticationFailedException {
    this.logger = securityLogger;
  }
  public Properties getCredentials(Properties securityProps,
      DistributedMember server, boolean isPeer)
      throws AuthenticationFailedException {
    Properties credentials = new Properties();
    String userName = securityProps.getProperty(USER_NAME);
    if (userName == null) {
      throw new AuthenticationFailedException(
          "SampleAuthInit: user name property [" + USER_NAME + "] not set.");
    }
    credentials.setProperty(USER_NAME, userName);
    String passwd = securityProps.getProperty(PASSWORD);
    if (passwd == null

"authenticate"方法是认证客户端或者Peer成员的回调。参数props提供连接成员的认证信息。这和Authinitialize类的getCredentials返回的属性是同样的。最后参数member提供连接客户端或者Peer的会员身份信息。

继续用简单的用户名/密码认证的例子,我们的认证示例使用JAAS(Java认证和授权服务)用户名/密码的实现来验证用户的身份凭据。首先我们需要为JAAS实现 CallbackHandler接口,这样就可以为JAAS登录模块提供用户名/密码 。下面的 SampleCallbackHandler类就是这样处理的。

Code Block
languagejava
 package com.gemstone.samples;
import java.io.IOException;
import java.security.Principal;
import java.util.Properties;
import java.util.Set;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import com.gemstone.gemfire.LogWriter;
import com.gemstone.gemfire.distributed.DistributedMember;
import com.gemstone.gemfire.security.AuthenticationFailedException;
import com.gemstone.gemfire.security.Authenticator;
public class SampleAuthenticator implements Authenticator {
  private LogWriter logger;
  private String jaasEntry;
  private LoginContext currentContext;
  public static final String JAAS_ENTRY = "security-jaas-entry";
  public static Authenticator create() {
      throwreturn new AuthenticationFailedExceptionSampleAuthenticator();
  }
  public void  init(Properties securityProps,  "SampleAuthInit: password property [" + PASSWORD + "] not set.");LogWriter systemLogger,
      LogWriter securityLogger) throws AuthenticationFailedException {
    }this.logger = securityLogger;
    credentials.setProperty(PASSWORD, passwdthis.jaasEntry = securityProps.getProperty(JAAS_ENTRY);
  }
   logger.info("SampleAuthInit: successfully obtained credentials for user "public Principal authenticate(Properties props, DistributedMember member)
      throws AuthenticationFailedException {
    SampleCallbackHandler callbackHandler =  + userNamenew SampleCallbackHandler(props);
    returntry credentials;{
  }
  public void close() {
  }
}  

The "close" method in the above implementation comes from the CacheCallbackinterface that AuthInitialize extends. The static "create" method is used to create an instance of the interface which is used for registration of the callback.

Authenticator

Next is the Authenticator interface that is required to be implemented on a server/peer/locator that will authenticate a new client or peer member. This callback is provided the credentials of the joining member as a set of properties as obtained fromAuthInitialize#getCredentials on the member.

Code Block
languagejava
public interface Authenticator extends CacheCallback {
 public void init(Properties securityProps, LogWriter systemLogger,
 LogWriter securityLogger) throws AuthenticationFailedException;
 public Principal authenticate(Properties props, DistributedMember member)
 throws AuthenticationFailedException;
}

 

This has a couple of methods (apart from the "close" method inherited fromCacheCallback interface). The first one "init" is used to perform any initialization of the callback and provided LogWriters useful for logging. The securityProps argument provides all the Geode properties of this member that start with the prefix "security-". For our sample implementation we will again use the securityLogger provided.

Code Block
languagejava
 public class SampleAuthenticator implements Authenticator {
  private LogWriter logger;
  public void init(Properties securityProps, LogWriter systemLogger,
      LogWriter securityLogger) throws AuthenticationFailedException {
    this.logger = securityLogger;
  }

The "authenticate" method is the guts of callback that authenticates the client or peer member. It is provided the credentials of the joining member as a set of Properties in theprops argument. This is the same set of properties that have been returned by theAuthInitialize#getCredentials on the member. Lastly the member argument provides the membership information of the joining client or peer member.

Continuing with the simple username/password based authentication example, our sample authenticator uses a JAAS username/password implementation to verify the credentials of the user. Firstly we need a CallbackHandler for JAAS that will just provide the username/password provided in the properties to the JAAS LoginModule. This is handled by the SampleCallbackHandler class below.

Code Block
languagejava
 package com.gemstone.samples;
import java.io.IOException;
import java.security.Principal;
import java.util.Properties;
import java.util.Set;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import com.gemstone.gemfire.LogWriter;
import com.gemstone.gemfire.distributed.DistributedMember;
import com.gemstone.gemfire.security.AuthenticationFailedException;
import com.gemstone.gemfire.security.Authenticator;
public class SampleAuthenticator implements Authenticator {
  private LogWriter logger;
  private String jaasEntry;
  private LoginContext currentContext;
  public static final String JAAS_ENTRY = "security-jaas-entry";
  public static Authenticator create() {
    return new SampleAuthenticator();
  }
  public void init(Properties securityProps, LogWriter systemLogger,
      LogWriter securityLogger) throws AuthenticationFailedException {
    this.logger = securityLogger;
    this.jaasEntry = securityProps.getProperty(JAAS_ENTRY);
  }
  public Principal authenticate(Properties props, DistributedMember member)
      throws AuthenticationFailedException {
    SampleCallbackHandler callbackHandler = new SampleCallbackHandler(props);
    try {
      this.currentContext = new LoginContext(this.jaasEntry, callbackHandler);
    } catch (LoginException ex) {
      throw new AuthenticationFailedException("SampleAuthenticator: failed "
          + "in creation of LoginContext for JAAS entry: "
          + this.jaasEntry, ex);
    }
    try {
      this.currentContext.login();
    } catch (LoginException ex) {
      throw new AuthenticationFailedException("SampleAuthenticator: "
          + "authentication failed for JAAS entry: " + this.jaasEntry, ex);
    }
    Set<Principal> principals = this.currentContext.getSubject()
        .getPrincipals();
    // assume only one Principal
    if (principals == null || principals.size() != 1) {
      throw new AuthenticationFailedException("SampleAuthenticator: expected "
          + "one Principal but got: " + principals);
    }
    logger.info("SampleAuthenticator: successfully authenticated member: "
        + callbackHandler.userName);
    return principals.iterator().next();
  }
  public void close() {
  }
  class SampleCallbackHandler implements CallbackHandler {
    private final String userName;
    private final String password;
    public SampleCallbackHandler(Properties props) {
      this.userName = props.getProperty(SampleAuthInit.USER_NAME);
      this.password = props.getProperty(SampleAuthInit.PASSWORD);
    }
    public void handle(Callback[] callbacks) throws IOException,
        UnsupportedCallbackException {
      for (Callback callback : callbacks) {
        if (callback instanceof NameCallback) {
          ((NameCallback)callback).setName(this.userName);
        }
        else if (callback instanceof PasswordCallback) {
          ((PasswordCallback)callback).setPassword(this.password.toCharArray());
        }
      }
    }
  }
}

The static "create" method is used to create an instance of the interface which is used for registration of the callback. Next, the configuration of the above two callbacks is discussed.

Configuration of authentication callbacks for peers

As mentioned before, a Geode Distributed System must use locators for discovery for peer security i.e. multicast discovery is incompatible with peer security settings discussed below.

A peer attempting to join a secure distributed system presents its credentials to one of the authenticated locators. The first locator to join the Distributed System is assumed to be authenticated and subsequent locators authenticate against the first one. The list of locators is obtained from the "locators" Geode property. The credentials obtained from the AuthIntialize#getCredentials method is sent to one of the locators for authentication. The security-peer-auth-init property should be set to the name of a zero argument static method that returns an AuthInitialize object on the members while the security-peer-authenticator property should be set to the name of zero argument static method that returns an Authenticator object on the members and locators. Note that since the members also authenticate the VIEW messages sent out, so all members also need to be configured with the Authenticator in addition to the locators.

The settings required for the above example implementations are:

security-peer-auth-init – com.gemstone.samples.SampleAuthInit.create

security-username – a valid user name

security-password – password for the above user

security-peer-authenticator – com.gemstone.samples.SampleAuthenticator.create

_security-jaas-entry _– entry name in JAAS configuration file to use for authentication

The JAAS configuration file should be provided using the normal "java.security.auth.login.config" System property. These need to be set on all the peer members and locators of the Distributed System. Sample code to do this programmatically is below:

Code Block
languagejava
    Properties props = new Properties();
    props.setProperty("security-peer-auth-init",
        "com.gemstone.samples.SampleAuthInit.create");
    props.setProperty(SampleAuthInit.USER_NAME, "user1");
    props.setProperty(SampleAuthInit.PASSWORD, "xxx");
    props.setProperty("security-peer-authenticator",
        "com.gemstone.samples.SampleAuthenticator.create");
    props.setProperty("security-jaas-entry", "Sample");
    DistributedSystem sys = DistributedSystem.connect(props);
 

If authentication for a peer member or locator fails, then the DistributedSystem.connect() method throws an AuthenticationFailedException. If the locators/peers have the security-peer-authenticator property set but the members do not have the security-peer-auth-initproperty set, then an AuthenticationRequiredException is thrown. All security exceptions have GemFireSecurityException as the base class so user code can choose to catch the base class exception where required.

Configuration of authentication callbacks for clients and servers

A client is authenticated for each handshake it initiates with a Geode cache server i.e. for each TCP connection from client to server. The client passes its credentials during the handshake and the server uses them to authenticate the client. The client must trust all the cache server host:bind-address[port] pairs in its endpoints list, since the client could connect to any server in the list and pass along its credentials. The credentials obtained from the AuthInitialize#getCredentials method is sent to the servers for authentication. The security-client-auth-init property should be set to the name of the zero argument static method that returns an AuthInitialize object on all the clients while the security-client-authenticator property should be set to the name of zero argument static method that returns an Authenticator object on all the servers.

The settings required for the above example implementations are:

For clients:

    this.currentContext = new LoginContext(this.jaasEntry, callbackHandler);
    } catch (LoginException ex) {
      throw new AuthenticationFailedException("SampleAuthenticator: failed "
          + "in creation of LoginContext for JAAS entry: "
          + this.jaasEntry, ex);
    }
    try {
      this.currentContext.login();
    } catch (LoginException ex) {
      throw new AuthenticationFailedException("SampleAuthenticator: "
          + "authentication failed for JAAS entry: " + this.jaasEntry, ex);
    }
    Set<Principal> principals = this.currentContext.getSubject()
        .getPrincipals();
    // assume only one Principal
    if (principals == null || principals.size() != 1) {
      throw new AuthenticationFailedException("SampleAuthenticator: expected "
          + "one Principal but got: " + principals);
    }
    logger.info("SampleAuthenticator: successfully authenticated member: "
        + callbackHandler.userName);
    return principals.iterator().next();
  }
  public void close() {
  }
  class SampleCallbackHandler implements CallbackHandler {
    private final String userName;
    private final String password;
    public SampleCallbackHandler(Properties props) {
      this.userName = props.getProperty(SampleAuthInit.USER_NAME);
      this.password = props.getProperty(SampleAuthInit.PASSWORD);
    }
    public void handle(Callback[] callbacks) throws IOException,
        UnsupportedCallbackException {
      for (Callback callback : callbacks) {
        if (callback instanceof NameCallback) {
          ((NameCallback)callback).setName(this.userName);
        }
        else if (callback instanceof PasswordCallback) {
          ((PasswordCallback)callback).setPassword(this.password.toCharArray());
        }
      }
    }
  }
}

静态方法“create”用于创建用于回调的注册的接口的实例。下面,探讨上述两个回调配置。

 

配置Peer之间的认证回调

 

如上所述,一个Geode分布式系统必须使用Locators寻找设置了安全的Peer。这与下面讨论的多路广播寻找设置了安全的Peer不相同。

一个Peer想要加入一个安全的分布式系统,必须呈现它的身份认证给一个已经认证的Locator。假定第一个加入到分布式系统的Locator被认证了,而后面的都没有。Locator列表是从Geode的"locators"属性获得。从AuthIntialize类的getCredentials 方法获得的身份证明被发送到一个Locator进行认证。在所有的Geode成员中security-peer-auth-init属性在一个返回AuthInitialize 对象的无参静态方法中应被设置成名字,而在所有成员和定位器中security-peer-authenticator属性在一个返回Authenticator对象的无参静态方法中应被设置成名字。注意由于成员认证的查看消息已经发出去,所以除了Locator所有成员还需要配置认证。

上面的示例实现所需的设置:

security-peersecurity-client-auth-init – com.gemstone.samples.SampleAuthInit.create

security-username – a valid user name

security-password – password for the above userFor servers:

security-clientpeer-authenticator – com.gemstone.samples.SampleAuthenticator.create

_security-jaas-entry  – _– entry name in JAAS configuration file to use for authentication

As before the JAAS configuration file should be provided using the normal JAAS配置文件应该使用正常系统属性"java.security.auth.login.config" System property. These need to be set on all the peer members and locators of the Distributed System. Sample code to do this programmatically is below:

For clients:

.login.config"。这些都需要在所有的Geode服务器和分布式系统的定位。示例代码来编程方式如下:

Code Block
languagejava
    Properties props = new Properties();
    props.setProperty("security-clientpeer-auth-init",
        "com.gemstone.samples.SampleAuthInit.create");
    props.setProperty(SampleAuthInit.USER_NAME, "user1");
    props.setProperty(SampleAuthInit.PASSWORD, "xxx");
    DistributedSystem sys = DistributedSystem.connect(props);

 

For servers:

Code Block
languagejava
 Properties props = new Properties(props.setProperty(SampleAuthInit.PASSWORD, "xxx");
    props.setProperty("security-clientpeer-authenticator",
        "com.gemstone.samples.SampleAuthenticator.create");
    props.setProperty("security-jaas-entry", "Sample");
    DistributedSystem sys = DistributedSystem.connect(props););

 

如果一个Peer成员或Locator认证失败,那么DistributedSystem.connect()方法抛出一个 AuthenticationFailedException。如果Locator/Peer 有设置security-peer-authenticator 属性但成员没有 设置security-peer-auth-initproperty属性,然后会抛出AuthenticationRequiredException。所有的安全异常有 GemFireSecurityException 为基类,所以用户代码可以在需要的地方选择捕获基类的异常。

 

配置Geode客户端和服务器之间的认证回调

 

客户端被认证是从与Geode缓存服务器的每一次握手开始,也是每一次客户端到服务器的TCP连接。在握手期间客户端递交它的身份认证信息,然后服务器用这些信息验证客户端。客户端必须信任那些和它的endpoints 列表中绑定的地址和端口号 host:bind-address[port] 一样的缓存服务器,因为客户端可以连接到列表中的任何服务器并且传递它的身份认证信息,那些从AuthInitialize类的getCredentials方法获得的身份认证信息被发送服务器进行认证。在所有的客户端上security-peer-auth-init属性在一个返回AuthInitialize 对象的无参静态方法中应该被设置成名字,而在所有的服务器上security-client-authenticator属性在一个返回Authenticator 对象的无参静态方法中应该被设置成名字。

上面的示例实现所需的设置:

对于客户端:

Unlike for peers, the authentication of clients is performed for each client-server connection that are created dynamically during Region operations. If authentication for a client fails, then the Region API method that requires to go to the server throws anAuthenticationFailedException. If the servers have the security-client-authenticatorproperty set but the clients do not have the security-client-auth-init property set, then anAuthenticationRequiredException is thrown by the Region API methods.

Interface for authorization callbacks

Authorization for cache operations is currently provided for clients that should first authenticate to the server as above. Once a client has authenticated to a server as above, the Principal _object returned by _Authenticator#authenticate method is associated to the client. This is then passed on to the authorization callback on the server, if any.

There are two places where authorization of cache operations can be performed: one in the pre-operation phase before an operation is performed, and second in the post-operation phase after the operation is complete on the server and before sending result back to the client (for get/query kind of operations that return a result). In addition, notifications sent to clients by servers are also authorized in the post-operation phase.

Code Block
languagejava
public interface AccessControl extends CacheCallback {
 public void init(Principal principal, DistributedMember remoteMember,
 Cache cache) throws NotAuthorizedException;
 public boolean authorizeOperation(String regionName, OperationContext context);
}

The "init" method is invoked to initialize the callback for a client. The Principal object obtained for the authenticated client (result of Authenticator#authenticate method) is passed as the first argument to the method. Membership information for the client is provided in the remoteMember argument, while the Geode Cache is passed as the third argument. The "authorizeOperation" method is invoked for each client cache operation on the authenticated connection. It is provided the region name of the operation and anOperationContext object that encapsulates information of the current cache operation.

 – com.gemstone.samples.SampleAuthInit.create

security-username – a valid user name

security-password – password for the above user

对于服务器:

security-client-authenticator – com.gemstone.samples.SampleAuthenticator.create

security-jaas-entry – entry name in JAAS configuration file to use for authentication

JAAS配置文件应该使用正常系统属性"java.security.auth.login.config"。这些都需要在所有的Peer服务器和分布式系统的Locators。示例代码来编程方式如下:

对于客户端:

Code Block
languagejava
    Properties props = new Properties();
    props.setProperty("security-client-auth-init",
        "com.gemstone.samples.SampleAuthInit.create");
    props.setProperty(SampleAuthInit.USER_NAME, "user1");
    props.setProperty(SampleAuthInit.PASSWORD, "xxx");
    DistributedSystem sys = DistributedSystem.connect(props);

对于服务器:

Code Block
languagejava
 Properties props = new Properties();
 props.setProperty("security-client-authenticator",
 "com.gemstone.samples.SampleAuthenticator.create");
 props.setProperty("security-jaas-entry", "Sample");
 DistributedSystem sys = DistributedSystem.connect(props);

不像Geode服务器,客户端的认证表现为在每一次客户端和服务器之间连接,这些连接都是在Region操作期间动态创建的。如果客户端认证失败,那么Region API方法需要在服务器抛出AuthenticationFailedException异常。如果服务器有security-client-authenticator属性设置,但是客户端没有security-client-auth-init设置,就会在Region API方法中抛出AuthenticationRequiredException 异常。

认证回调接口

首先对服务器进行身份验证的当前客户端提供缓存操作的授权。首先一旦客户端认证了服务器,Authenticator#authenticate方法返回的Principal 对象与客户端相关。如果有,然后传递给服务器上的授权回调。

有两个授权缓存操作的地方:一个在操作前的预备操作阶段,另一个在后置操作阶段,也就是在服务器完成认证操作之后,返回结果给客户端之前(返回的结果是查询的操作类型)。此外,被服务器发送到客户端的通知也在后置操作阶段被授权。

Code Block
languagejava
public interface AccessControl extends CacheCallback {
 public void init(Principal principal, DistributedMember remoteMember,
 Cache cache) throws NotAuthorizedException;
 public boolean authorizeOperation(String regionName, OperationContext context);
}

“init”方法被调用了用来为客户端初始化回调。Principal 对象获得了授权过的客户端(Authenticator#authenticate方法的返回值),它被作为这个方法的第一个参数。客户端的会员信息在参数remoteMember 中有提供,同时第三个参数Cache 提供Geode缓存。当在缓存授权连接操作的时候”authorizeOperation”方法会被执行。该方法需要提供操作的region的名字和封装了当前缓存操作的OperationContext 对象。

继续我们的用户名/密码示例,我们假设在上面的示例实现中JAAS授权模块返回Principal (自定义的Principal 对象),它提供”isReader”方法,如果成员只被赋予只读权限,该方法返回true,而其他成员拥有全部权限,预授权回调的代码如下:Continuing with our username/password example, we assume that the JAAS authentication module in above sample implementations returns a Principal (MyPrincipalclass) that provides an "isReader" method that will return true if the member should be given read-only permissions, while other members have all permissions. The code for pre-authorization callback is below:

Code Block
languagejava
package com.gemstone.samples;
import java.security.Principal;
import com.gemstone.gemfire.cache.Cache;
import com.gemstone.gemfire.cache.operations.OperationContext;
import com.gemstone.gemfire.cache.operations.OperationContext.OperationCode;
import com.gemstone.gemfire.distributed.DistributedMember;
import com.gemstone.gemfire.security.AccessControl;
import com.gemstone.gemfire.security.NotAuthorizedException;
public class SampleAccessControl implements AccessControl {
 private boolean isReader;
 public static AccessControl create() {
 return new SampleAccessControl();
 }
 public void init(Principal principal, DistributedMember remoteMember,
 Cache cache) throws NotAuthorizedException {
 if (principal instanceof MyPrincipal) {
 this.isReader = ((MyPrincipal)principal).isReader();
 }
 else {
 this.isReader = false;
 }
 }
 public boolean authorizeOperation(String regionName, OperationContext context) {
 if (this.isReader) {
 OperationCode opCode = context.getOperationCode();
 // these cache operations do not modify data
 return (opCode.isGet() || opCode.isQuery() || opCode.isContainsKey()
 || opCode.isKeySet() || opCode.isRegisterInterest()
 || opCode.isUnregisterInterest() || opCode.isExecuteCQ()
 || opCode.isCloseCQ() || opCode.isStopCQ());
 }
 else {
 return true;
 }
 }
 public void close() {
 }
}
 

The "init" method caches the "isReader" flag for the client since the isReader method may be potentially expensive. The "authorizeOperation" method then allows read-only operations when the "isReader" flag is true.

Configuration of authorization callbacks

 

“init”方法为客户端缓存了”isReader”方法的标识,因为isReader方法可能有潜在风险。当isReader标识为true时authorizeOperation方法然后才允许只读操作

授权回调配置

服务器的设置要求如上面的示例实现所示:The setting required on the servers for the above example implementation is:

security-client-access-control – com.gemstone.samples.SampleAccessControl.create

If the authorization for a cache operation is denied by the server (i.e. authorizeOperationmethod on server returns false), then the client receives a NotAuthorizedException for the operation.

如果为一个被服务器拒绝的缓存操作授权(例如authorizeOperation方法在服务器上返回false),那么这个客户端会因为这个操作收到一个NotAuthorizedException 异常。

阿德翻译

...