此内容没有您所选择的语言版本。
14.2. Custom Modules
AuthenticationManager
requires a particular usage pattern of the Subject
principals set. You must understand the JAAS Subject class's information storage features and the expected usage of these features to write a login module that works with the AuthenticationManager
.
LoginModule
implementations that can help you implement custom login modules.
Subject
by using the following methods:
java.util.Set getPrincipals() java.util.Set getPrincipals(java.lang.Class c) java.util.Set getPrivateCredentials() java.util.Set getPrivateCredentials(java.lang.Class c) java.util.Set getPublicCredentials() java.util.Set getPublicCredentials(java.lang.Class c)
Subject
identities and roles, EAP has selected the most logical choice: the principals sets obtained via getPrincipals()
and getPrincipals(java.lang.Class)
. The usage pattern is as follows:
- User identities (for example; user name, social security number, employee ID) are stored as
java.security.Principal
objects in theSubject
Principals
set. ThePrincipal
implementation that represents the user identity must base comparisons and equality on the name of the principal. A suitable implementation is available as theorg.jboss.security.SimplePrincipal
class. OtherPrincipal
instances may be added to theSubject
Principals
set as needed. - Assigned user roles are also stored in the
Principals
set, and are grouped in named role sets usingjava.security.acl.Group
instances. TheGroup
interface defines a collection ofPrincipal
s and/orGroup
s, and is a subinterface ofjava.security.Principal
. - Any number of role sets can be assigned to a
Subject
.
- The EAP security framework uses two well-known role sets with the names
Roles
andCallerPrincipal
.- The
Roles
group is the collection ofPrincipal
s for the named roles as known in the application domain under which theSubject
has been authenticated. This role set is used by methods like theEJBContext.isCallerInRole(String)
, which EJBs can use to see if the current caller belongs to the named application domain role. The security interceptor logic that performs method permission checks also uses this role set. - The
CallerPrincipal
Group
consists of the singlePrincipal
identity assigned to the user in the application domain. TheEJBContext.getCallerPrincipal()
method uses theCallerPrincipal
to allow the application domain to map from the operation environment identity to a user identity suitable for the application. If aSubject
does not have aCallerPrincipal
Group
, the application identity is the same as operational environment identity.
14.2.1. Subject Usage Pattern Support
Subject
usage patterns described in Section 14.2, “Custom Modules”, EAP includes login modules that populate the authenticated Subject
with a template pattern that enforces correct Subject
usage.
The most generic of the two is the org.jboss.security.auth.spi.AbstractServerLoginModule
class.
javax.security.auth.spi.LoginModule
interface and offers abstract methods for the key tasks specific to an operation environment security infrastructure. The key details of the class are highlighted in Example 14.20, “AbstractServerLoginModule Class Fragment”. The JavaDoc comments detail the responsibilities of subclasses.
Important
loginOk
instance variable is pivotal. This must be set to true
if the log in succeeds, or false
by any subclasses that override the log in method. If this variable is incorrectly set, the commit method will not correctly update the subject.
Example 14.20. AbstractServerLoginModule Class Fragment
package org.jboss.security.auth.spi; /** * This class implements the common functionality required for a JAAS * server-side LoginModule and implements the PicketBox standard * Subject usage pattern of storing identities and roles. Subclass * this module to create your own custom LoginModule and override the * login(), getRoleSets(), and getIdentity() methods. */ public abstract class AbstractServerLoginModule implements javax.security.auth.spi.LoginModule { protected Subject subject; protected CallbackHandler callbackHandler; protected Map sharedState; protected Map options; protected Logger log; /** Flag indicating if the shared credential should be used */ protected boolean useFirstPass; /** * Flag indicating if the login phase succeeded. Subclasses that * override the login method must set this to true on successful * completion of login */ protected boolean loginOk; // ... /** * Initialize the login module. This stores the subject, * callbackHandler and sharedState and options for the login * session. Subclasses should override if they need to process * their own options. A call to super.initialize(...) must be * made in the case of an override. * * <p> * The options are checked for the <em>password-stacking</em> parameter. * If this is set to "useFirstPass", the login identity will be taken from the * <code>javax.security.auth.login.name</code> value of the sharedState map, * and the proof of identity from the * <code>javax.security.auth.login.password</code> value of the sharedState map. * * @param subject the Subject to update after a successful login. * @param callbackHandler the CallbackHandler that will be used to obtain the * the user identity and credentials. * @param sharedState a Map shared between all configured login module instances * @param options the parameters passed to the login module. */ public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { // ... } /** * Looks for javax.security.auth.login.name and * javax.security.auth.login.password values in the sharedState * map if the useFirstPass option was true and returns true if * they exist. If they do not or are null this method returns * false. * Note that subclasses that override the login method * must set the loginOk var to true if the login succeeds in * order for the commit phase to populate the Subject. This * implementation sets loginOk to true if the login() method * returns true, otherwise, it sets loginOk to false. */ public boolean login() throws LoginException { // ... } /** * Overridden by subclasses to return the Principal that * corresponds to the user primary identity. */ abstract protected Principal getIdentity(); /** * Overridden by subclasses to return the Groups that correspond * to the role sets assigned to the user. Subclasses should * create at least a Group named "Roles" that contains the roles * assigned to the user. A second common group is * "CallerPrincipal," which provides the application identity of * the user rather than the security domain identity. * * @return Group[] containing the sets of roles */ abstract protected Group[] getRoleSets() throws LoginException; }
The second abstract base login module suitable for custom login modules is the org.jboss.security.auth.spi.UsernamePasswordLoginModule
.
char[]
password as the authentication credentials. It also supports the mapping of anonymous users (indicated by a null user name and password) to a principal with no roles. The key details of the class are highlighted in the following class fragment. The JavaDoc comments detail the responsibilities of subclasses.
Example 14.21. UsernamePasswordLoginModule Class Fragment
package org.jboss.security.auth.spi; /** * An abstract subclass of AbstractServerLoginModule that imposes a * an identity == String username, credentials == String password * view on the login process. Subclasses override the * getUsersPassword() and getUsersRoles() methods to return the * expected password and roles for the user. */ public abstract class UsernamePasswordLoginModule extends AbstractServerLoginModule { /** The login identity */ private Principal identity; /** The proof of login identity */ private char[] credential; /** The principal to use when a null username and password are seen */ private Principal unauthenticatedIdentity; /** * The message digest algorithm used to hash passwords. If null then * plain passwords will be used. */ private String hashAlgorithm = null; /** * The name of the charset/encoding to use when converting the * password String to a byte array. Default is the platform's * default encoding. */ private String hashCharset = null; /** The string encoding format to use. Defaults to base64. */ private String hashEncoding = null; // ... /** * Override the superclass method to look for an * unauthenticatedIdentity property. This method first invokes * the super version. * * @param options, * @option unauthenticatedIdentity: the name of the principal to * assign and authenticate when a null username and password are * seen. */ public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { super.initialize(subject, callbackHandler, sharedState, options); // Check for unauthenticatedIdentity option. Object option = options.get("unauthenticatedIdentity"); String name = (String) option; if (name != null) { unauthenticatedIdentity = new SimplePrincipal(name); } } // ... /** * A hook that allows subclasses to change the validation of the * input password against the expected password. This version * checks that neither inputPassword or expectedPassword are null * and that inputPassword.equals(expectedPassword) is true; * * @return true if the inputPassword is valid, false otherwise. */ protected boolean validatePassword(String inputPassword, String expectedPassword) { if (inputPassword == null || expectedPassword == null) { return false; } return inputPassword.equals(expectedPassword); } /** * Get the expected password for the current username available * via the getUsername() method. This is called from within the * login() method after the CallbackHandler has returned the * username and candidate password. * * @return the valid password String */ abstract protected String getUsersPassword() throws LoginException; }
The choice of sub-classing the AbstractServerLoginModule
versus UsernamePasswordLoginModule
is based on whether a string-based user name and credentials are usable for the authentication technology you are writing the login module for. If the string-based semantic is valid, then subclass UsernamePasswordLoginModule
, otherwise subclass AbstractServerLoginModule
.
The steps your custom login module must execute depend on which base login module class you choose. When writing a custom login module that integrates with your security infrastructure, you should start by sub-classing AbstractServerLoginModule
or UsernamePasswordLoginModule
to ensure that your login module provides the authenticated Principal
information in the form expected by the EAP security manager.
AbstractServerLoginModule
, you must override the following:
void initialize(Subject, CallbackHandler, Map, Map)
: if you have custom options to parse.boolean login()
: to perform the authentication activity. Be sure to set theloginOk
instance variable to true if log in succeeds, false if it fails.Principal getIdentity()
: to return thePrincipal
object for the user authenticated by thelog()
step.Group[] getRoleSets()
: to return at least oneGroup
namedRoles
that contains the roles assigned to thePrincipal
authenticated duringlogin()
. A second commonGroup
is namedCallerPrincipal
and provides the user's application identity rather than the security domain identity.
UsernamePasswordLoginModule
, you must override the following:
void initialize(Subject, CallbackHandler, Map, Map)
: if you have custom options to parse.Group[] getRoleSets()
: to return at least oneGroup
namedRoles
that contains the roles assigned to thePrincipal
authenticated duringlogin()
. A second commonGroup
is namedCallerPrincipal
and provides the user's application identity rather than the security domain identity.String getUsersPassword()
: to return the expected password for the current user name available via thegetUsername()
method. ThegetUsersPassword()
method is called from withinlogin()
after thecallbackhandler
returns the user name and candidate password.