|
Related Reading
WebLogic: The Definitive Guide |
Editor's note: Last week's excerpt from Chapter 17 of WebLogic: The Definitive Guide examined WebLogic's security mechanisms, including the Java Security Manager. In this week's second and final excerpt from the book, authors Avinash Chugh and Jon Mountjoy cover WebLogic's various security providers and their default implementations, along with a look at how to authenticate using JAAS, and examples of Authentication and Identity Assertion Providers.
Now we'll take a closer look at the different SSPIs that constitute a security realm. We'll learn about WebLogic's default implementation of these security providers and how to configure them. The default implementation provides the authentication architecture (and much more) that we have just seen. You can replace one or more of the providers with your own code if you want to change its behavior. Once again, the Administration Console lets you view and modify the configuration of these security providers. All of the security providers available to your realm can be found under the Security/Realms/myrealm/Providers node in the left pane of the Administration Console, where "myrealm" refers to the name of the security realm. Finally, we'll learn about the embedded LDAP server that holds all of the security data for the domain on behalf of the default security providers.
Authentication refers to the server's ability to reliably verify the identity of a user or system. We generally refer to a user or system being authenticated as simply a user. A user requires some proof of identity before it can establish trust with the server. WebLogic supports Authentication Providers that can validate user credentials based on a username-password combination or a digital certificate. The security provider repository, which stores the user and group information, can be implemented in the following ways:
WebLogic's authentication provider architecture closely follows the authentication
part of the standard JAAS. Following the JAAS terminology, a subject represents the
source of a security request—it represents a user or system that is trying to be
authenticated. The point of authentication is to assign principals to a subject. Principals
are identities that represent the result of a successful authorization. As we have
seen, when the system user authenticates successfully, the subject representing this
user is assigned a principal recording the fact that he is in the Administrators group,
and another recording that he is a WebLogic user with the name system. Therefore, a
subject is a standard container for authentication information, including principals.
A client can use a subject to query its identity and other attributes. Figure 17-2 illustrates
this client authentication process.

Figure 17-2. The client authentication process
The Authentication Provider adheres to the standard JAAS framework by structuring the authentication sequence on top of a number of configurable JAAS LoginModules. LoginModules are critical components of an Authentication Provider, as they are responsible for authenticating users within a security realm and populating the subject with the required principals (existing users and groups).
WebLogic demands that you have at least one authentication provider configured in your security realm.
Once a (possibly remote) client has established trust with WebLogic, the authenticated subject is retained on the client between server invocations. The Principal Validation Provider ensures that no hanky-panky has taken place with the subject's principals at any time between these invocations. It does this by signing and verifying the authenticity of the principals held by the subject. Once the principals have been validated, they can be used by an Authorization Provider for access control checks or by the Role Mapping Provider for role-mapping decisions. A security realm must define a Principal Validation Provider for each Authentication Provider.
Identity Assertion Providers help secure access to the entry points of a WebLogic deployment. Instead of using usernames and passwords, an external client may use tokens to establish trust with a WebLogic Server. The Identity Assertion Provider verifies a token and, if successful, maps it to a valid WebLogic user. Once the token is mapped to a valid user, an Authentication Provider can then generate the principals for the user. This mechanism is called perimeter authentication, so you can consider an Identity Assertion Provider a special type of Authentication Provider. The key point here is that an external agent is responsible for authenticating the user, and then for conveying the user data to WebLogic.
As a side effect, perimeter authentication also enables single sign-on. For instance, an Identity Assertion Provider could supply an X.509 digital certificate as an identity token, and these credentials then could be used across multiple systems. WebLogic supports Identity Assertion Providers that can handle different token types (X.509, IIOP-CSIv2). Alternatively, you can create an Identity Assertion Provider that supports custom token types (e.g. Kerberos tickets). An Identity Assertion Provider can have multiple active token types. However, a token type can be active only in a single provider. Of course, a security realm may support multiple Identity Assertion Providers, though none is required. Figure 17-3 illustrates perimeter authentication.

Figure 17-3. Identity assertion during perimeter authentication
WebLogic supports authentication based on username-password combinations, authentication using digital certificates transmitted either directly to WebLogic or via an HTTP server, and perimeter-based authentication based on security tokens. WebLogic lets you use the Authentication Providers described next.
In addition, you may assign custom-built Authenticators and Identity Asserters to the security realm.
In order to access the Authentication Providers assigned to a realm, select the Authentication node from the left pane of the Administration Console. The right pane then lets you select one of the configured Authenticators, or configure a new Authenticator.
A security realm can have a sequence of Authentication Providers (and hence JAAS
LoginModules) working in unison. Each Authentication Provider has a JAAS control
flag setting that determines how the overall login sequence behaves with respect to
that particular Authentication Provider. This is no different from how you configure
multiple LoginModules within a J2SE application. To order the Authentication Providers
within a security realm, select the Authentication node from the left pane and
choose the Re-order the Configured Authentication Providers option. The control
flag of each authentication provider can take the following values:
REQUIREDREQUIRED provider fails. Thus, REQUIRED providers are always invoked, and overall
authentication fails if any one of them fails.REQUISITEREQUISITE providers need not be invoked for
the overall authentication to succeed. If a REQUISITE provider succeeds, the
authentication proceeds as normal to other providers in the sequence. However,
if it fails, the overall authentication cannot succeed, and control is immediately
passed back to the application once all REQUIRED providers in the login sequence
have been invoked.SUFFICIENTSUFFICIENT provider does succeed, the overall authentication
proceeds to ensure that only the remaining REQUIRED providers in the login
sequence are executed. However, if it fails, the overall authentication proceeds as
normal to the other providers in the login sequence.OPTIONALOPTIONAL provider succeeds, the
authentication proceeds to other providers that have been configured as part of
the login sequence.When multiple LoginModules are used to authenticate a user, the authentication
occurs in two phases. During the first phase, the modules are asked to attempt to
authenticate the user. Only if the modules pass this phase is the second phase
invoked. During the second phase, each module commits the login and assigns the
relevant principals to the subject. The control flags influence how this two-phase
commit occurs. Thus, for the overall authentication to succeed, the following rules
must be met:
REQUIRED modules must be invoked, and each must successfully validate the user.REQUISITE module that gets invoked must successfully validate the user.SUFFICIENT module successfully validates the user, the overall success
depends on the success of all REQUIRED modules, and any REQUISITE modules
invoked before the SUFFICIENT module.OPTIONAL modules, at least one module
must successfully validate the user.Though you can write your own Authenticators, WebLogic's default implementation of the SSPIs comes with a number of built-in authenticators that you can use. Let's look at these authenticators in more detail.
|
The Default Authenticator authenticates against the embedded LDAP repository. It provides the notion of a user and group and stores user and group information in its own repository, allowing you to manipulate this information. The only configurable option provided by this Authenticator is the Minimum Password Length setting. By default, all WebLogic users must specify a password that is at least eight characters in length; the password length is validated when the user is created. When you log on to the Administration Server using the console or through a JNDI context, it is generally this authenticator that validates the username and passwords you supply. Of course, the control flag will determine exactly which providers are called.
WebLogic also lets you configure an Authentication Provider that can use existing external LDAP directories such as iPlanet LDAP, Active Directory, Open LDAP, and Novell NDS. In fact, WebLogic's LDAP Authenticators can interface with any LDAP v3-compliant directory servers. In this section, we look at how you can set up an iPlanet Authenticator for your security realm. Other LDAP Authenticators can be configured along the same lines. By using one of the LDAP Authenticators, you can configure WebLogic to recognize users and groups defined in the LDAP repositories and authenticate against this information.
First, select the Authentication node under the realm from the left frame of the
Administration Console. If you're using the default security realm myrealm, then the
node will be under Security/Realms/myrealm/Providers. Then, select the "Configure a
new iPlanet Authenticator" option, either by right-clicking the node or from the right
frame itself. Now under the General tab on the right, you'll see the overall details of
the new Authenticator. Choose a name for the Authenticator and make sure that the
value of the JAAS Control Flag is Required, and then hit the Create button.
Note: When starting out, it may be useful to set the Control Flag to Optional. In this way, even if your LDAP authentication fails, you still can authenticate using WebLogic's default authenticator and gain access to the Administration Console.
Now select the iPlanet LDAP tab, and enter the values for the host, port, and principal
as they apply to your LDAP server. Here, the principal refers to the distinguished
name (DN) of the LDAP user that WebLogic will use to connect to the LDAP Server.
Typically, you'll use the DN associated with some administrative user account on the
LDAP server. For iPlanet LDAP, this is usually uid=admin, ou=Administrators,
ou=TopologyManagement, o=NetscapeRoot. If the LDAP server is listening on an SSL
port, tick the SSL Enabled option and ensure that you've specified the SSL port. Now
hit the Apply button to save changes to the form. Next, change the Credential
attribute for the LDAP Principal. In the new screen, you must enter the password
that will be used to authenticate the LDAP user defined in the Principal attribute.
Now, select the Users tab and make sure the fields in this form match the configuration
of your LDAP repository. In most cases, you will need to modify only the value
of the User Base DN attribute to ou=people, o=mydomain.com. This attribute defines
the base DN of the branch within the LDAP tree that holds the actual users.
Table 17-3 lists the other configuration settings under the Users tab.
Table 17-3. Configuring the Users tab for an LDAP Authenticator
| Setting | Description | Default |
|---|---|---|
| User Object Class | This attribute indicates the LDAP object class that holds user information | person |
| User Name Attribute | This setting specifies the name of the attribute within the LDAP user object that holds the username. | uid |
| User Search Scope | This setting determines how the users are organized in a multilevel hierarchy or a flat, single-level tree. It affects how deep in the hierarchy to search for users. You can choose from the following values: subtree/onelevel. | subtree |
| User From Name Filter | This attribute specifies a search filter for finding a user given the username. | "(&(uid=%u)(objectclass=person))" |
WebLogic populates these fields with sensible default values, so in most cases you
won't have to alter them. Hit the Apply button and move on to the Groups tab.
Again, you need to ensure the settings accurately reflect the structure of your LDAP
repository. In most cases, you will need to modify only the Group Base DN setting to
(say) ou=groups, o=mydomain.com. Table 17-4 lists the other configuration settings
available under the Groups tab.
Table 17-4. Configuring the Groups tab for an LDAP Authenticator
| Setting | Default |
|---|---|
| Static Group Object Class | groupofuniquenames |
| Static Group Name Attribute | cn |
| cn Group Search Scope | subtree |
| Group From Name Filter | "(|(&(cn=%g)(objectclass=groupofUniqueNames))(&(cn=%g)(objectclass=groupOfURLs)))" |
These settings are pretty straightforward, and they have the same semantics as the attributes under the Users tab, just applied to groups.
The Membership tab determines how group members are stored and located in the LDAP directory. WebLogic specifies default values for all fields in the form. Table 17-5 lists two important attributes under the Membership tab.
Table 17-5. Configuring the Membership tab for an LDAP Authenticator
| Setting | Description | Default |
|---|---|---|
| Static Member DN Attribute | This setting specifies the name of the attribute within the LDAP group object that holds the DNs of members of the group. | member |
| Static Group DNs from Member DN Filter | This attribute specifies a search filter for finding all groups that contain the member, given the name of the group member. | (&(uniquemember=%M)(objectclass=groupofuniquenames)) |
Unlike the other authentication providers, the iPlanet provider also supports dynamic groups for which there are additional options.
Now that you have configured the LDAP Authenticator, you are almost ready to reboot the Administration Server. However, you need to ensure that the server's boot identity (i.e., the WebLogic user account used to start the server) corresponds to an LDAP user with the necessary Admin privileges. This means you need to use the iPlanet Console (or any management tool specific to the LDAP server) and complete the following steps:
Create an Administrators group in the LDAP repository and place the LDAP user, which is associated with WebLogic's boot identity, in this group.
If you are unable to create an Administrators group, create a new group in the LDAP Repository—say, MyAdministrators. Make the LDAP user, which is associated with WebLogic's boot identity, a member of the MyAdministrators group. Then using WebLogic's Administration Console, assign the MyAdministrators group to the default global role Admin.
By doing this, you guarantee that WebLogic's boot identity has the required Admin privileges. The next time you restart the server, go into the Administration Console and remove the Default Authenticator from the list of Authentication Providers. Once you've applied these changes, restart the server—hopefully, you should be able to boot without any authentication errors and set up your WebLogic domain to use the LDAP repository for its user and group information base.
The Default Identity Asserter supports perimeter authentication using either X.509
certificates or IIOP CORBA Common Secure Interoperability Version 2 (CSIv2)
tokens. A good example of perimeter authentication is when you configure a web
application to use CLIENT-CERT authentication. In this case, WebLogic can perform
identity assertion based on values from request headers and cookies. If the header
name or cookie name matches the active token type for the provider, the value is
passed to the provider.
This provider requires you to configure the following attributes:
weblogic.security.providers.authentication.
UserNameMapper interface, and also must be available in WebLogic's CLASSPATH
during startup.Note that Identity Assertion Providers do not verify proof material. A user can forge a CSIv2 token, for instance, and assume a false identity. Because WebLogic cannot trust such tokens, the Trusted Client Principals setting offers a way to restrict the set of client principals that can use identity assertion. We saw a good example of using X.509 certificates as perimeter authentication in the previous chapter, where we also supplied a custom username mapper class for mapping digital certificates to WebLogic users. When using two-way SSL and X.509 certificates for identity assertion, the SSL protocol ensures that the certificate is not forged. So, the Trusted Client Principals setting applies only to CSIv2 identity tokens.
WebLogic 8.1 comes with a default username mapper that you can enable from the Details tab. This is a general username mapper that can be configured to extract a username from a given attribute of the subject DN field in a certificate. So, for example, if the client's certificate has an email attribute (E), then you can set the Default User Name Mapper Attribute Type to E. You also can specify a delimiter, in which case WebLogic will use that part of the attribute up to but not including the delimiter. For instance, if you need to extract a username from the email attribute, you will want to use the delimiter @. Other attribute types that can be used are the C, CN, L, O, OU, S, and ST types. If you need anything more complex than this, you will have to create your own username mapper class. See Example 17-2 later in this chapter to learn how you can write a custom username mapper.
|
Authorization is synonymous with access control—it determines whether a subject has access to a resource. Whenever an application requests an operation on a protected resource, the resource container that receives the request calls the WebLogic Security framework to determine whether the user is authorized to access the resource. In making this call, any relevant request parameters, such as the subject making the request, are passed to the framework. A few things need to happen before an access decision can be made, as illustrated in Figure 17-4.
First, the configured Role Mapping Providers are invoked. These providers use the request parameters to determine a set of roles that are valid for the subject. After this, the Authorization Providers are asked to decide whether access should be allowed. The Adjudication Provider has the final say in the matter. It looks at the different access decisions returned by the Authorization Providers and reconciles any potential conflicts. The Adjudication Provider generates a final verdict based on these individual access decisions.
A realm may include one or more Authorization Providers. For instance, you could define Authorization Providers that separately control access to JNDI branches, web applications, JMS connection factories, and more.

Figure 17-4. The authorization process
WebLogic's Authorization Provider performs the aforementioned tasks by using a policy-based authorization engine. This means that WebLogic's resources are protected via security policies assigned at deployment time. Earlier, in the section "The Security Provider Architecture," we looked at how your security policy statements can help protect server-side resources.
Security policies may be assigned manually through the Administration Console, or automatically via the role settings specified in deployment descriptors. The Default Authorizer lets you specify whether the authorizer will store policies that are created when EJBs and web applications are deployed. The Policy Deployment Enabled option indicates whether the provider will evaluate security policies while EJBs and web applications are being deployed. By default, this setting is enabled. This option is quite similar to the Role Deployment Enabled flag in the Role Mapping Provider.
A Role Mapping Provider determines dynamically at call time the set of roles that are valid for a particular subject when it tries to access a protected resource. An Authorization Provider can then use this information to determine whether a user is allowed access to the resource by evaluating the policy and role information.
The role information is generally based on the role settings defined in J2EE and WebLogic-specific deployment descriptors, the requested operation on the resource, and any custom business logic. You can configure these roles at deployment time, either using the deployment descriptors or via the Administration Console. Because the Authorization Provider makes calls to the Role Mapping Provider just before calculating an authorization decision, and because the role binding occurs dynamically, this gives you the ideal opportunity to embed your own logic for role allocation into a custom provider.
A security realm can define one or more Role Mapping Providers. You could configure a Role Mapping Provider that handles role associations for resources bound to the JNDI tree, or another that handles role associations for web applications.
Given a protected resource and a user, WebLogic's default Role Mapping Provider will determine all the roles that are valid for that user for the resource using the role information stored in its repository. In order to configure the Default Role Mapper, select the Default Role Mapper node from under the Role Mapping node in the left pane of the Administration Console.
The Default Role Mapper has one configuration setting only: the Role Deployment Enabled option. This indicates whether the provider stores roles that are created while deploying a web application or EJB. This setting is similar to the Ignore Security Data in Deployment Descriptors setting that we encountered earlier, except that it is specific to role information. If the Role Deployment Enabled option is set, WebLogic automatically creates roles based on security data in the deployment descriptors and stores them in the embedded LDAP server. This means that if you subsequently ignore the security data in the deployment descriptors, the deployment descriptors won't be processed the next time the EJB or web application is deployed. By default, the Role Deployment Enabled setting is enabled, and you need at least one role mapper with this setting enabled in order to deploy web applications and EJBs.
A subject may access a protected resource in a way that requires multiple Authorization Providers to decide whether the subject has access to the protected resource. Each Authorization Provider may pass one of the following access decisions: PERMIT, ABSTAIN, or DENY. The Adjudication Provider needs to arbitrate between potentially conflicting decisions made by the different Authorization Providers involved. Typically, the Adjudication Provider resolves potential authorization conflicts among the Authorization Providers by carefully weighing the access decisions of each provider.
Thus, you must define an Adjudication Provider if the security realm supports multiple Authorization Providers. Clearly, a security realm may have only a single Adjudication Provider!
In order to configure the Default Adjudicator, select the Default Adjudicator node from under the Adjudication node in the left frame of the Administration Console.
Here you can adjust the Requires Unanimous Permit setting, which determines the basis on which the Adjudicator grants access to a resource when multiple Authorization Providers are concerned.
If this setting is enabled, the Adjudicator grants access to the resource only if all participating
Authorization Providers return a result of PERMIT. That is, the decision
must be a unanimous PERMIT decision. If any provider returns a DENY or ABSTAIN, the
overall decision must be to refuse access.
If the setting is disabled, the Adjudicator grants access to the resource only if no
Authorization Provider returns a DENY. This means that the Adjudicator will grant
access to the resource even though a unanimous decision could not be reached. So
long as all Authorization Providers vote to PERMIT or ABSTAIN, the subject will be
allowed to access the resource. Of course, at least one Authorization Provider must
vote to PERMIT access to the resource.
When a WebLogic user needs to access some external systems, the credentials of the user need to be mapped to valid credentials on the external system. Only then can a subject already authenticated by WebLogic log into the external system. A Credential Mapping Provider is responsible for associating users authenticated by WebLogic to appropriate credentials in an external system. Typically, the Credential Mapping Provider is invoked by WebLogic on behalf of another component—in particular, the container that hosts a resource adapter that needs to connect to a remote resource.
A Credential Mapping Provider can handle different kinds of user credentials—e.g., a username-password combination, a digital certificate, and more. For instance, you could implement a credential map between WebLogic users and credentials of valid users on a remote legacy DBMS. This could be the username-password combination of a valid user on the remote system who is authorized to perform the necessary operations. You can define these credential mappings either in the deployment descriptors or via the Administration Console. A security realm must define at least one Credential Mapping Provider. If you've defined multiple providers, WebLogic queries all providers and returns a list of credentials that may be associated with the WebLogic user.
WebLogic's Credential Mapper associates WebLogic users to appropriate credentials in an external system when a user makes use of a resource adapter. So, the Credential Mapping Provider holds a map of WebLogic users and groups to external identities that can be used to authenticate to a remote system. A good example of this is the use of J2EE connectors. Here you typically need to map a WebLogic user to a remote user that has access to the target EIS. Chapter 7 shows how to configure credential maps for deployed J2EE connectors.
In order to configure the provider itself select the Default Credential Mapper node from under Providers/Credential Mapping in your security realm. The Default Credential Mapper provides only one configuration setting, the Credential Mapping Deployment Enabled option, which indicates whether the provider processes the credential maps from the descriptor files of a resource adapter when it is deployed. By default, the Credential Mapping Deployment Enabled flag is set to true. Like the other Deployment Enabled settings, if you disable this setting, the credential maps specified in the deployment descriptor for the resource adapter won't overwrite the credential maps you may have created via the Administration Console.
Auditing is another important feature of WebLogic's security framework. Nonrepudiation requires that you maintain a log of security events that provides an electronic trace of how the data has been accessed. WebLogic's Auditing Provider can be configured to log information about security requests and their outcomes. Usually, the Auditing Provider is invoked by WebLogic's Auditor on behalf of other security providers, both before and after a security operation has been executed. In this way, the Auditor is able to capture detailed information about any security requests and responses.
Typically, the Auditing Provider will decide whether to audit the security request based on several criteria. For instance, you could configure a security level for the Auditing Provider that automatically filters out audit requests that do not reach the desired security level. An Auditing Provider also supports channels that can record the information to a variety of sinks, such as an LDAP store, database table, or plain file. WebLogic's Auditor can interact with multiple Auditing Providers. However, you may choose not to define any Auditing Provider for a security realm.
By default, there are no Auditing Providers configured for a security realm. In order to configure a new Auditing Provider, expand the Auditing node from the left frame of the Administration Console and then choose the "Configure a new Default Auditor" option. In the new screen, assign a severity level for the audit log and hit the Create button.
The severity level determines the kind of events that are logged by WebLogic's
Default Auditor. The lower the severity level, the more verbose the auditing. The
severity level can be chosen from the following (in increasing order of verbosity):
FAILURE, SUCCESS, ERROR, WARNING, and INFORMATION. For example, a severity level of
FAILURE ensures that the Auditor logs only security requests that have failed (e.g.,
failed authentication), while a severity level of INFORMATION ensures that the Auditor
logs all authentication activity. The default severity level is ERROR.
Note: The lower the severity level, the more verbose the output. This can impact server performance because the Auditor is potentially a lot busier. You should set a sensible severity level and carefully monitor your setup to ensure it doesn't yield unacceptable performance levels.
The audit information is written to the mydomain\myserver\DefaultAuditRecorder.log file, where mydomain is the name of the WebLogic domain. For example, if an authenticated user attempts to dip into a connection pool and is denied access, the Default Auditor logs the following information:
#### Audit Record Begin <05-Sep-02 22:24:46> <Severity =FAILURE>
<<<Event Type = Authorization Audit Event ><Subject: 1
Principal = class weblogic.security.principal.WLSUserImpl("A")>
<ONCE><<jdbc>><type=<jdbc>, application=, module=, resourceType=ConnectionPool,
resource=MyPool, action=reserve>>> Audit Record End ####
|
Any implementation of the SSPIs requires some kind of security provider database that can act as a repository for the domain's security data. WebLogic relies on an embedded LDAP server to persist all of its information about users, groups, policies, roles, and user credentials. The embedded LDAP server is accessible to all of WebLogic's security providers that need to store and manipulate such data: the Authentication, Authorization, Role Mapping, and Credential Mapping Providers. In the default setup, the Administration Server holds a master LDAP repository, and this repository is then replicated to all Managed Servers. Any changes made by WebLogic's providers are sent to the master LDAP server, which then sends the appropriate changes to each replicated server. Managed Servers in the domain are synchronized as soon as the data is changed on the Administration Server's LDAP repository. There is, however, a small window between the write to the Administration Server and the replication due to network traffic.
To configure the embedded LDAP server, select your domain from the left frame of the Administration Console. Choose the View Domain-Wide Security Settings* option and select the Configuration/Embedded LDAP tab. Table 17-6 lists the various settings that can be configured from the Embedded LDAP tab.
* WebLogic 7.0 users should just choose the Security/Embedded LDAP tab after selecting the domain node.
Table 17-6. Configuring the embedded LDAP server
| Setting | Description | Default |
|---|---|---|
| Credential | This setting specifies a password that allows you to connect to the LDAP server. | none |
| Backup Hour/Minute | This setting determines the hour/minute when the backups are supposed to occur. | 23;5 |
| Backup Copies | This setting determines the number of backup copies of the LDAP data that should be made. | 7 |
| Cache Enabled | This option indicates whether caching is enabled for the LDAP server. | true |
| Cache Size | This setting determines the size of the cache used by the LDAP server. | 32KB |
| Cache TTL | This setting determines the duration for which items are held in the cache. | 60s |
| Replica Refresh | By default, changes are sent periodically to the Managed Servers. If you've made a number of changes while a Managed Server has been down, then sending a large number of changes may be expensive. To optimize this, enabling this parameter ensures all of the replicated data will be refreshed as a whole at boot time. | false |
| Master First | In extreme cases, you can enable this flag so that any Managed Server must contact the master LDAP server instead of its local LDAP server. | false |
Note how the LDAP server can be configured to back itself up. The backup files are written to a directory called mydomain\servername\ldap\backup and can be used to replace those files in the ldapfiles directory.
You can use your favorite LDAP browser to gain access to WebLogic's embedded LDAP server. This may be useful for integration purposes, or perhaps more usefully as providing a way of importing and exporting user data. Before accessing the server, you need to set up its security credential. Select the Embedded LDAP tab as described in the previous section. Then select the Credential option and enter a credential (password) for the LDAP server. WebLogic will use this password to authenticate any LDAP clients.
Now start up your favorite LDAP browser and point it to the following URL:
ldap://hostname:port/dc=mydomain
Here, mydomain refers to the name of the WebLogic domain. The LDAP server isn't set
up for anonymous access, so you must specify the username cn=Admin. For the password,
you need to supply the same credentials you configured earlier for WebLogic's
embedded LDAP server. You then can browse the LDAP data, which includes information
about the users, groups, security roles, policy statements, and more.
Some LDAP browsers also let you import and export the LDAP data in the LDIF format.
This provides you with an easy way to migrate some data, such as the information
on users and groups located under the DNs ou=people,ou=myrealm,dc=mydomain and
ou=groups,ou=myrealm,dc=mydomain, respectively. Once again, myrealm refers to the
name of the security realm, while mydomain refers to the name of the WebLogic
domain. Experienced administrators can further restrict access to the embedded LDAP
server. Because WebLogic's LDAP server also supports the IETF LDAP Access Control
Model, you can implement fine-grained access to the LDAP server if the need arises.
In WebLogic 8.1, many security providers also permit the export and import of their data. You can find this facility on the Migration tab of the Authentication, Authorization, Credential Mapper, and Role Mapper Providers.
Two or more WebLogic domains can be configured so that they trust each other. When you've set up trust between two different WebLogic domains, you enable authenticated WebLogic users in one domain to access protected resources in another domain. For instance, an authenticated user in one domain may invoke a protected web service or an EJB in another domain without requiring any additional authentication. Let's say that we have two WebLogic domains, A and B. Trust between the domains means that the subject's principals in one domain—say, A— are accepted by the other domain, B (and vice versa) and are treated as if they were local principals of domain B. The Authorization Providers within domain B remain unaware of the fact that the subject's principals belong to domain A.
Two WebLogic domains will trust each other only if they both have the same Credential attribute. The Credential attribute represents a string value that is assigned to a domain and then is used to sign principals that belong to subjects created in that domain. Ordinarily, if you haven't explicitly set the domain credential, it is assigned a random value when the Administration Server is first booted. All Managed Servers in the domain subsequently import this credential when they are booted as well. In order to set the Credential attribute for a WebLogic domain, select your domain from the left pane of the Administration Console, choose the View Domain-Wide Security Settings* option, and select the Configuration/Advanced tab. If two domains share the same Credential attribute value, the signatures of the principals will match and be recognized by both domains. Thus, in order to establish trust between several domains, you need to simply ensure they are assigned the same value for the Credential attribute.
We already have seen many examples of how a Java client authenticates itself to WebLogic Server. In most cases, the client submits a username-password combination as its credentials when setting up the JNDI context:
Hashtable env = new Hashtable( );
env.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL, "t3://10.0.10.10:7001");
env.put(Context.SECURITY_PRINCIPAL, "system");
env.put(Context.SECURITY_CREDENTIALS, "12341234");
Context ctx = new InitialContext(env);
// use the JNDI context as "system" user ...
WebLogic also lets you build Java clients that can use the more standard approach to authentication using JAAS. Even though JAAS authentication is somewhat more long-winded than traditional JNDI-based authentication, your clients will be more portable. Because of the pluggable nature of the JAAS framework, it should enable you to benefit from future changes to the authentication technology without changes to the client code.
A JAAS client involves the interplay among a number of classes and interfaces, as shown in Figure 17-5. Let's examine how these different objects interact during JAAS-style authentication:
SubjectLoginContextSubject with its principals. Its allimportant
login( ) method delivers an authenticated Subject back to the client.
To construct a LoginContext instance, you need to supply objects of two subsidiary
classes: a CallBackHandler and LoginModule instance.CallBackHandlerCallBackHandler
instance could conceivably pop up a dialog box requesting the data from the end
user. In fact, the CallBackHandler instance is invoked by the LoginModule.LoginModuleThis is any entity capable of authenticating the user's credentials. A separate
JAAS configuration file settles how the LoginModule is implemented. In general,
you can implement your own login modules. However, WebLogic also provides
you with a convenient LoginModule that can authenticate the client via the supplied
username and password against a WebLogic instance whose URL is also
supplied. On successful authentication, the LoginModule populates the subject
with its principals. Using this authenticated subject, the JAAS client can now
perform one or more privileged actions.

Figure 17-5. Typical interaction when authenticating a JAAS client
PrivilegedActionPrivilegedAction interface encapsulates the code
that a Java client can run within the security context defined by the populated
Subject. The weblogic.security.Security.runAs( ) method allows the client to
associate a Subject with the current thread, before invoking the privileged action
under this security context.
|
Let's look at how to build a JAAS client that can authenticate itself to WebLogic.
We'll cover this example using a top-down approach, starting with what the JAAS
client needs to accomplish and then breaking it down into the individual components
of its implementation. Let's begin with the main class, SimpleJAASClient,
which takes the following steps:
Example 17-1 lists the source code for our JAAS client.
Example 17-1. A sample JAAS client
package com.oreilly.wlguide.security.jaas;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
public class SimpleJAASClient {
public static void main(String[] args) {
String username = args[0];
String password = args[1];
String url = args[2];
LoginContext loginContext = null;
// Create a LoginContext using our own CallBackHander
try {
loginContext = new LoginContext("Simple",
new SimpleCallbackHandler(username, password, url));
} catch (Exception e) {
// Can get a SecurityException or a LoginException
e.printStackTrace( );
System.exit(-1);
}
// Now authenticate. If we don't get an exception, we succeeded
try {
loginContext.login( );
} catch (Exception e) {
// Can get FailedLoginException, AccountExpiredException,
// or CredentialExpiredException
e.printStackTrace( );
System.exit(-1);
}
// Retrieve authenticated subject and perform action using it
Subject subject = loginContext.getSubject( );
SimpleAction simpleAction = new SimpleAction(url);
weblogic.security.Security.runAs(subject, simpleAction);
}
}
Notice how we've highlighted the important bits of the JAAS client. Our first critical
step is to establish a LoginContext object:
loginContext = new LoginContext("Simple",
new SimpleCallbackHandler(username, password, url));
The LoginContext object initializes the client with the CallBackHandler and
LoginModule instances that will be used during JAAS authentication. The second
argument to the constructor is our own CallBackHandler instance that will be used by
the LoginModule to retrieve the user's credentials, and the URL of the WebLogic
instance that will authenticate our client.
The first argument to the constructor, Simple, is used to look up the appropriate
LoginModule for the client. JAAS clients rely on a configuration file that maps the
names of JAAS login modules to their implementation, and also may specify additional
parameters. Example 17-2 lists the JAAS configuration file that we used.
Example 17-2. Login configuration file, jaas.config
Simple {
weblogic.security.auth.login.UsernamePasswordLoginModule
required
};
Our configuration file contains a single entry for Simple that specifies WebLogic's
LoginModule for authentication on the basis of the given username and password:
weblogic.security.auth.login.UsernamePasswordLoginModule. When you run the
JAAS client, you must specify the location of this configuration file using a system
property. Here's how you would run our sample JAAS client:
java -Djava.security.auth.login.config=jaas.config \
com.oreilly.wlguide.security.jaas.SimpleJAASClient system pssst t3://10.0.10.10:
8001/
In this way, we can configure the LoginContext to use WebLogic's LoginModule,
which supports authentication using a username-password combination. Later, we'll
see how you can use the JAAS configuration file to transparently substitute this with
your own LoginModule implementation.
After establishing the login context, we've invoked the loginContext.login( )
method to execute the actual login. Our LoginContext will utilize the configured
login module and callback handler objects and attempt to authenticate the client
with the server. If this client is authenticated successfully, you can retrieve the
authenticated subject from the LoginContext:
Subject subject = loginContext.getSubject( );
The getPrincipals( ) method on this authenticated Subject retrieves all of the principals
associated with the user. For instance, if our JAAS client authenticated using
the credentials of the system administrator, the authenticated Subject holds two
principals: system, which represents the user, and Administrators, which represents
the user's group. Now, we can use this subject to execute one or more "privileged"
actions. In other words, these actions are performed within the context of this
authenticated subject:
weblogic.security.Security.runAs(subject, simpleAction);
There is one caveat here—the client must invoke the runAs( ) method on
WebLogic's Security class. The runAs( ) method accepts two parameters: the
authenticated Subject, and a PrivilegedAction object, which wraps the applicationspecific
interaction with the server. Example 17-3 illustrates the action that our JAAS
client wishes to execute.
Example 17-3. A very simple action
package com.oreilly.wlguide.security.jaas;
import java.security.PrivilegedAction;
import java.sql.Connection;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
public class SimpleAction implements PrivilegedAction {
private static final String JNDI_NAME = "jdbc.xpetstore";
private String url;
public SimpleAction(String url) {
this.url = url;
}
public Object run( ) {
Object obj = null;
try {
Context ctx = null;
Hashtable ht = new Hashtable( );
ht.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
ht.put(Context.PROVIDER_URL, url);
// Get a context for the JNDI lookup
ctx = new InitialContext(ht);
// do any work here
DataSource ds =(javax.sql.DataSource) ctx.lookup(JNDI_NAME);
// ...
} catch (Exception e) {
e.printStackTrace( );
}
return obj;
}
}
Here you need to recognize the following significant points:
The class implements the java.security.PrivilegedAction interface. Any JAAS
client can then invoke an instance of this class within the context of the authenticated
Subject.
The run( ) method encapsulates the client's interaction with the server. Typically,
the client will establish a JNDI context, use it to grab resources bound to
the JNDI tree, and then invoke/access these resources. In the earlier example, we
used the JNDI context to acquire a JDBC data source.
When we establish the JNDI context within the PrivilegedAction.run( )
method, we don't provide any user credentials for JNDI authentication. The
authenticated Subject supplied by the JAAS client to the runAs( ) method
ensures that the PrivilegedAction object is invoked within the context of this
subject. That is, the runAs( ) method is responsible for associating the authenticated
Subject with the current thread.
Example 17-4 lists the source code for our CallBackHandler class. In general, the callback
handler would interact with the client in some way that prompts the user for
the username and password to be used for authentication. In the case of our simple
JAAS client, we supply the necessary credentials and URL to the constructor of our
callback handler so that the callbacks can easily return this information.
Example 17-4. A simple callback handler
package com.oreilly.wlguide.security.jaas;
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 weblogic.security.auth.callback.URLCallback;
public class SimpleCallbackHandler implements CallbackHandler {
private String username = null;
private String password = null;
private String url = null;
public SimpleCallbackHandler(String pUsername, String pPassword, String pUrl) {
username = pUsername; password = pPassword; url = pUrl;
}
public void handle(Callback[] callbacks)
throws java.io.IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
NameCallback nc = (NameCallback) callbacks[i];
nc.setName(username);
} else if (callbacks[i] instanceof URLCallback) {
URLCallback uc = (URLCallback) callbacks[i];
uc.setURL(url);
} else if (callbacks[i] instanceof PasswordCallback) {
PasswordCallback pc = (PasswordCallback) callbacks[i];
pc.setPassword(password.toCharArray( ));
} else {
throw new UnsupportedCallbackException(
callbacks[i], "Unrecognized Callback");
}
}
}
}
The final piece of the puzzle is the JAAS login module. Earlier, we saw how the JAAS
configuration file enabled us to set up our client to use WebLogic's login module for
username-password authentication, the UsernamePasswordLoginModule. This Login-Module class expects our callback handler to deal with username and password callbacks,
and optionally, the URL callback as well. The login( ) method provides the
entry point for the JAAS framework into the LoginModule. It uses the user's credentials
to authenticate the client with WebLogic and, if successful, returns an authenticated
Subject populated with the appropriate principals.
We could have easily constructed our own login module and modified the configuration
file to reference this module. The login( ) method is the most important within
the LoginModule implementation class because this method is responsible for performing
the actual authentication. Typically, it must use the configured callback handler
to retrieve the username, password, and URL. It must then create an
Environment object populated with this data, and invoke the authenticate( ) method
on WebLogic's Authenticate class to execute the login and generate an authenticated
Subject populated with required principals. The following code shows how to
accomplish this authentication:
weblogic.jndi.Environment env = new weblogic.jndi.Environment( );
env.setProviderUrl(url);
env.setSecurityPrincipal(username);
env.setSecurityCredentials(password);
weblogic.security.auth.Authenticate.authenticate(env, subject);
In general, WebLogic's login module should be sufficient for most purposes; it is
unlikely that you will need to provide your own LoginModule implementation.
|
Developing your own security providers is a relatively specialized task—it is necessary only if WebLogic's default providers are insufficient. The majority of custom providers tend to change the default authentication or identity assertion mechanisms. The following sections provide an example of each of these. We recommend that you read WebLogic's well-documented security provider API to understand the life cycle of each provider if you intend to create your own. This information is supplied on the official web sit. BEA's dev2dev web site also contains a number of example providers.
WebLogic's provider architecture is MBean-based (see Chapter 20)—if you are going to write a new provider, it has to have a corresponding MBean implementation. WebLogic provides tools for creating the necessary MBean deployment files and implementations. At runtime, the MBean representing your provider will be used to create an instance of your provider implementation—the MBean is, in a sense, a factory for the provider implementation that you will have to supply. This, in turn, will use the MBean to read its configuration information. Any provider MBean must extend the appropriate base MBean type, supplied with WebLogic. To facilitate in the creation of these peripheral classes, WebLogic provides a few utilities. They are based around an MBean Definition File (MDF), an XML file that describes your MBean implementation. Example 17-5 lists such an XML file.
Example 17-5. A simple MDF (MyAuthentication.xml) for an authentication provider
<?xml version="1.0" ?>
<!DOCTYPE MBeanType SYSTEM "commo.dtd">
<MbeanType Name = "MyAuthenticator" DisplayName = "MyAuthenticator"
Package = "com.oreilly.wlguide.security.iap"
Extends = "weblogic.management.security.authentication.Authenticator"
PersistPolicy = "OnUpdate">
<MbeanAttribute Name = "ProviderClassName" Type = "java.lang.String"
Writeable = "false"
Default =
""com.oreilly.wlguide.security.iap.MyAuthenticationProviderImpl""
/>
<MbeanAttribute Name = "Description" Type = "java.lang.String"
Writeable = "false"
Default = ""O'Reilly Authentication Provider""
/>
<MBeanAttribute Name = "Version" Type = "java.lang.String"
Writeable = "false" Default = ""1.0""
/>
</MBeanType>
If you are to implement your own authentication provider, copy Example 17-5 verbatim,
changing only the lines that have been highlighted (unless you want to add additional
attributes). The ProviderClassName MBean attribute is the most important.
This indicates the class name that represents the custom provider that we will have
to implement, in this case, com.oreilly.wlguide.security.iap.MyAuthentication-ProviderImpl.
Once you have this file, you can use the MbeanMaker utility to take the MDF file and
generate the MBean and stubs:
java -DcreateStubs="true" weblogic.management.commo.WebLogicMBeanMaker -MDF ..\
MyAuthentication.xml -files outdirectory
This generates a number of files, all of which can be ignored unless you implement custom operations and attributes. These are all placed in the outdirectory. All you need to do now is supply the actual implementation of the provider, recompile and repackage the whole lot, and deploy it.
Note that to use these utilities, you not only need to execute the setEnv script to prepare your environment, but you also have to add an extra JAR file to your classpath. This JAR file is located in WL_HOME/server/lib/mbeantypes/wlManagement.jar.
Now we turn to the authentication provider implementation itself. Recall that in the
MDF, we specified the class name of a class that is to supply the authentication provider
(see Example 17-6). This class must implement the weblogic.security.spi.AuthenticationProvider interface.
Example 17-6. An authentication provider implementation
package com.oreilly.wlguide.security.iap;
import java.util.HashMap;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;
import weblogic.management.security.ProviderMBean;
import weblogic.security.provider.PrincipalValidatorImpl;
import weblogic.security.spi.*;
public final class MyAuthenticationProviderImpl implements AuthenticationProvider {
private String description;
private LoginModuleControlFlag controlFlag;
// Our mapping of users to passwords/groups, instead of being in LDAP or in a
// database, is represented by a HashMap of MyUserDetails objects..
public class MyUserDetails {
String pw;
String group;
// We use this to represent the user's groups and passwords
public MyUserDetails(String pw, String group) {
this.pw=pw; this.group = group;
}
public String getPassword( ) {return pw;}
public String getGroup( ) {return group;}
}
// This is our database
private HashMap userGroupMapping = null;
public void initialize(ProviderMBean mbean, SecurityServices services) {
MyAuthenticatorMBean myMBean = (MyAuthenticatorMBean)mbean;
description = myMBean.getDescription( ) + "\n" + myMBean.getVersion( );
System.err.println("#In realm:" + myMBean.getRealm( ).wls_getDisplayName( ));
// We would typically use the realm name to find the database
// we want to use for authentication. Here, we just create one.
userGroupMapping = new HashMap( );
userGroupMapping.put("a", new MyUserDetails("passworda", "g1"));
userGroupMapping.put("b", new MyUserDetails("passwordb", "g2"));
userGroupMapping.put("system", new MyUserDetails("12341234",
"Administrators"));
String flag = myMBean.getControlFlag( );
if (flag.equalsIgnoreCase("REQUIRED")) {
controlFlag = LoginModuleControlFlag.REQUIRED;
} else if (flag.equalsIgnoreCase("OPTIONAL")) {
controlFlag = LoginModuleControlFlag.OPTIONAL;
} else if (flag.equalsIgnoreCase("REQUISITE")) {
controlFlag = LoginModuleControlFlag.REQUISITE;
} else if (flag.equalsIgnoreCase("SUFFICIENT")) {
controlFlag = LoginModuleControlFlag.SUFFICIENT;
} else {
throw new IllegalArgumentException("Invalid control flag " + flag);
}
}
public AppConfigurationEntry getLoginModuleConfiguration( ) {
HashMap options = new HashMap( );
options.put("usermap", userGroupMapping);
return new AppConfigurationEntry(
"com.oreilly.wlguide.security.provider.MyLoginModuleImpl",
controlFlag, options
);
}
public String getDescription( ) {
return description;
}
public PrincipalValidator getPrincipalValidator( ) {
return new PrincipalValidatorImpl( );
}
public AppConfigurationEntry getAssertionModuleConfiguration( ) {
return null;
}
public IdentityAsserter getIdentityAsserter( ) {
return null;
}
public void shutdown( ) {}
}
The class simply provides a way of getting to the various modules, most of which are
optional. For example, we do not provide an identity asserter, so we just return null.
Likewise, we simply return WebLogic's default principal validator implementation as
the principal validator. All of the action happens in the initialize( ) and
getLoginModuleConfiguration( ) methods.
Typically, the job of the initialize( ) method is to set up the resources necessary to
perform any authentication. As you can see from the implementation, it has direct
access to the MBean—so, if you added extra attributes or operations to the MBean,
they can be used here. In our case, we simply set up a local database in the form of a
hashmap. It then stores the control flag that is used in the module configuration.
This eventually will get passed to the login module.
The getLoginModuleConfiguration( ) method returns an object that encapsulates the
login module—the final piece of functionality that we have to implement. You can
think of this method as playing the same role as the JAAS configuration file. It is
instructing the provider as to which login module to use, on the server side this time.
Note that the login module is wrapped in an object (AppConfigurationEntry) that also
captures the control flag and options.
|
We now need to supply a login module, as shown in Example 17-7. This follows the standard JAAS API.
Example 17-7. A login module
package com.oreilly.wlguide.security.provider;
import java.io.IOException;
import java.util.*;
import javax.security.auth.Subject;
import javax.security.auth.callback.*;
import javax.security.auth.login.*;
import javax.security.auth.spi.LoginModule;
import weblogic.security.principal.WLSGroupImpl;
import weblogic.security.principal.WLSUserImpl;
/**
* This login module will be called by our Authentication Provider.
* It assumes that the option, usermap, will be passed which contains
* the map of users to passwords and groups.
*/
public class MyLoginModuleImpl implements LoginModule {
private Subject subject;
private CallbackHandler callbackHandler;
private HashMap userMap;
// Authentication status
private boolean loginSucceeded;
private boolean principalsInSubject;
private Vector principalsBeforeCommit = new Vector( );
public void initialize(Subject subject, CallbackHandler callbackHandler,
Map sharedState, Map options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
// Fetch user/password map that should be set by the authenticator
userMap = (HashMap) options.get("usermap");
}
/* Called once after initialize to try and log the person in */
public boolean login( ) throws LoginException {
// First thing we do is create an array of callbacks so that
// we can get the data from the user
Callback[] callbacks;
callbacks = new Callback[2];
callbacks[0] = new NameCallback("username: ");
callbacks[1] = new PasswordCallback("password: ", false);
try {
callbackHandler.handle(callbacks);
} catch (IOException eio) {
throw new LoginException(eio.toString( ));
} catch (UnsupportedCallbackException eu) {
throw new LoginException(eu.toString( ));
}
String username = ((NameCallback) callbacks[0]).getName( );
char [] pw = ((PasswordCallback) callbacks[1]).getPassword( );
String password = new String(pw);
if (username.length( ) > 0) {
if (!userMap.containsKey(username))
throw new FailedLoginException("Authentication Failed: Could not find user:
" + username);
String realPassword = ((MyAuthenticationProviderImpl.MyUserDetails) userMap.
get(username)).getPassword( );
if (realPassword == null || !realPassword.equals(password))
throw new FailedLoginException("Authentication Failed: Password incorrect
for user" + username);
} else {
// No Username, so anonymous access is being attempted
}
loginSucceeded = true;
// We collect some principals that we would like to add to the user
// once this is committed.
// First, we add his username itself
principalsBeforeCommit.add(new WLSUserImpl(username));
// Now we add his group
principalsBeforeCommit.add(new WLSGroupImpl(((MyAuthenticationProviderImpl.
MyUserDetails)userMap.get(username)).getGroup( )));
return loginSucceeded;
}
public boolean commit( ) throws LoginException {
if (loginSucceeded) {
subject.getPrincipals().removeAll(principalsBeforeCommit);
principalsInSubject = true;
return true;
} else {
return false;
}
}
public boolean abort( ) throws LoginException {
if (principalsInSubject) {
subject.getPrincipals( ).removeAll(principalsBeforeCommit);
principalsInSubject = false;
}
return true;
}
public boolean logout( ) throws LoginException {
return true;
}
}
Although long, this is pretty straightforward. Let's go through it. The initialize( )
method has direct access to the options that were configured in the provider. As we
put our database in the options, this method provides the ideal place to extract the
database. The rest of the action occurs in a combination of the login( ), commit( ),
and abort( ) methods. If a login succeeds, and the control flags of the login modules
are such that the entire login is to commit, the commit( ) method will be called—
otherwise, the abort( ) method will be called. These methods simply ensure that the
principals that should be associated with the subject are placed into the subject. The
login( ) method does all of the work. First, it sets up a number of callbacks—we
need the username and password. Note that the actual callback implementation is
going to be handled by WebLogic. For example, WebLogic prompts you for the system
user credentials when you try and boot a WebLogic server or access the Administration
Console. It is the data from these callbacks that eventually will be supplied
to the login method. After handling the callbacks, we extract the username and password
of the user and locate it in our database. If successful, we make a list of principals
that we want to associate with the user, storing these in the variable
principalsBeforeCommit. The principals are added to the subject only if WebLogic
calls the commit( ) method.
Once you have created the authentication provider and login module, you can package these together with the generated stub and MBI files. To do this, execute the following command:
java weblogic.management.commo.WebLogicMBeanMaker -MJF myAuth.jar -files .
You are now ready to deploy your new provider. Copy myAuth.jar to the WL_HOME/server/lib/mbeantypes directory, and then reboot the server. Note that all custom providers
have to be located in this directory. Start up the Administration Console and
navigate to the Security/myrealm/Providers/Authentication node. In the list of available
authenticators and identity asserters, you should find an option for "Configure a
new My Authenticator." Selecting this option and clicking Create will configure the
authenticator. On the following tab, you will notice that you can change the control
flag. If you change this to something such as requisite, make sure that your database
has a user in the Administrators group. If not, you won't even be able to boot the
server! Use the OPTIONAL flag during development to avoid these problems.
Imagine that you have some external system—say, a Java client or perhaps even an external web server—that authenticates a user, and you now want this user to participate in actions involving WebLogic. Furthermore, you don't want WebLogic to reauthenticate the user. Rather, you want to use some token generated by the external system to be used as an automatic WebLogic login. This is fairly typical of many single sign-on scenarios. The key to implementing this is to use an Identity Assertion Provider. Let's look at how you can implement such a scenario.
We are going to take as an example an external Java client that has presumably performed
some user authentication, and who now needs to transfer this identity to
WebLogic in order to access a protected web application. First of all, let's configure
the web application to use identity assertion. Do this by setting the login-config to
use a CLIENT-CERT authorization method. As this is standard J2EE, you will need to
create a web.xml file with something such as the following in it:
<security-constraint>
<!-- web resource collection omitted -->
<auth-constraint>
<description>nyse</description>
<role-name>mysecrole</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>CLIENT-CERT</auth-method>
<realm-name>myrealm</realm-name>
</login-config>
<security-role>
<role-name>mysecrole</role-name>
</security-role>>
Now let's imagine we have a client (written in whatever language you wish) that has already performed some user authentication and now needs to access one of the protected web pages—say, http://10.0.10.10:8001/index.jsp. The following client is such an example:
URL url = new URL("http://10.0.10.10:8001/index.jsp");
URLConnection connection = url.openConnection( );
BufferedReader in = new BufferedReader(new InputStreamReader(
connection.getInputStream( )));
// Read the input stream
in.close( );
|
If you simply run this program, you can expect an IOException when you try and
access the input stream. This will be the 401 HTTP error code indicating that you are
not authorized. We are going to get around this by making the client supply a token,
and then configuring an Identity Assertion Provider to accept this token and authorize
the user. Identity Assertion Providers can automatically take advantage of
request cookies or headers. If WebLogic finds, for example, a header property with
the same name as a token (we will see in a moment how to configure the identity
provider with token names), it assumes that the content of the header property is the
value of the token. The token we will use is a simple string that we are going to send
in the HTTP request header when we create the connection to the server. To this
end, modify the preceding code to read as follows:
URL url = new URL(urlAddr);
URLConnection connection = url.openConnection( );
connection.setRequestProperty("MyToken",encodedToken);
// Everything as before
The name of the request property, MyToken in our example, is significant. This is
interpreted as the type of the token, which we will see later. A small caveat here is
that WebLogic always expects incoming tokens to be Base 64-encoded. You can do
this by using the utility class weblogic.utils.encoders.BASE64Encoder. So, to create
an encoded token, you can write something such as this:
String token = "jon";
BASE64Encoder encoder = new BASE64Encoder( );
String encodedToken = encoder.encodeBuffer(token.getBytes( ));
The text that you place in the token can be anything you please, as long as your Identity Assertion Provider can read it. In our example, we will use a simple string, which we take to represent the authenticated user.
Note: WebLogic 8.1 allows you to configure the Identity Assertion Provider to use tokens that aren't encoded, in which case you won't need to use an encoder.
All that's left now is to create an Identity Assertion Provider. The MBean definition file used in our example is given in Example 17-8 in full.
Example 17-8. MyA.xml, the MDF file for the assertion provider
<?xml version="1.0" ?>
<!DOCTYPE MBeanType SYSTEM "commo.dtd">
<MBeanType Name = "MyA" DisplayName = "MyA"
Package = "com.oreilly.wlguide.security.iap"
Extends = "weblogic.management.security.authentication.IdentityAsserter"
PersistPolicy = "OnUpdate"
>
<MBeanAttribute Name = "ProviderClassName" Type = "java.lang.String"
Writeable = "false"
Default = ""com.oreilly.wlguide.security.iap.MyAProviderImpl""
/>
<MBeanAttribute Name = "Description" Type = "java.lang.String"
Writeable = "false" Default = ""My Identity Assertion Provider""
/>
<MBeanAttribute Name = "Version" Type = "java.lang.String"
Writeable = "false" Default = ""1.0""
/>
<MBeanAttribute Name = "SupportedTypes" Type = "java.lang.String[]"
Writeable = "false" Default = "new String[] { "MyToken" }"
/>
<MBeanAttribute Name = "ActiveTypes" Type = "java.lang.String[]"
Default = "new String[] { "MyToken" }"
/>
</MBeanType>
Note the following things:
weblogic.management.security.authentication.IdentityAsserter MBean as indicated.ProviderClassName attribute must be set to the implementation
class.SupportedTypes attribute must be set to the token type. In our case, this is
MyToken.ActiveTypes attribute lists the subset of the provider's supported types that
you want active. Because we want our only token active, we set it to MyToken as
well.You can create the support files as usual. Here we place all the output in the directory out:
java -DcreateStubs="true" weblogic.management.commo.WebLogicMBeanMaker -MDF MyA.xml
-files out
Finally, you need to create the provider class com.oreilly.wlguide.security.iap.MyAProviderImpl, which was referred to in the ProviderClassName attribute.
Example 17-9 lists this class in its entirety.
Example 17-9. The provider implementation
package com.oreilly.wlguide.security.iap;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.AppConfigurationEntry;
import weblogic.management.security.ProviderMBean;
import weblogic.security.spi.*;
public final class MyAProviderImpl
implements AuthenticationProvider, IdentityAsserter {
private String description; // holds our description which we derive from MBean
attributes
public void initialize(ProviderMBean mbean, SecurityServices services) {
MyAMBean myMBean = (MyAMBean)mbean;
description = myMBean.getDescription( ) + "\n" + myMBean.getVersion( );
}
public CallbackHandler assertIdentity(String type, Object token)
throws IdentityAssertionException {
if (type.equals("MyToken")) {
byte[] tokenRaw = (byte[])token;
String username = new String(tokenRaw);
return new SimpleSampleCallbackHandlerImpl(username,null,null);
} else
throw new IdentityAssertionException("Strange Token!");
}
public String getDescription( ) {
return description;
}
public void shutdown( ) {
}
public IdentityAsserter getIdentityAsserter( ) {
return this; // this object is the identity asserter
}
public AppConfigurationEntry getLoginModuleConfiguration( ) {
return null; // we are not an authenticator
}
public AppConfigurationEntry getAssertionModuleConfiguration( ) {
return null; // we are not an authenticator
}
public PrincipalValidator getPrincipalValidator( ) {
return null; // we are not an authenticator
}
}
}
The most important methods are initialize( ) and assertIdentity( ). The
initialize() method simply extracts some information from the MBean representing
the provider and uses it to create the description. The assertIdentity( ) method
is given two parameters, the type of the token and the token itself. We simply check
that the token type is correct and map the token to the username. You could conceivably
do a lot more here, such as validate the authenticity of the token for stronger
security. The method must return a standard JAAS callback handler, which
eventually will be invoked to extract the username (that is, only the NameCallback will
be used). We use the callback handler that we defined in Example 17-4. Note that
the identity asserter could have been an authenticator too, in which case it could
populate the subject with usernames and groups belonging to the user. Because we
are doing pure identity assertion, the corresponding methods simply return null.
Place this file and the callback handler in the out directory, and then issue the following command to create a packaged provider:
java weblogic.management.commo.WebLogicMBeanMaker -MJF myIAP.jar -files out
Copy this to the WL_HOME/server/lib/mbeantypes directory, and then reboot the server. Start up the Administration Console and navigate to the Security/myrealm Providers/Authentication node. In the list of available authenticators and identity asserters, you should find an option for "Configure a new MyA...". Selecting this option and clicking Create will configure the identity asserter. On the following tab you will notice that the support token type is set to MyToken and the active token to MyToken too. You will now have to reboot the server for this change to take effect.
If you rerun the client application, you will find that you will no longer get an unauthorized
warning (assuming that jon is in the permission group mysecrole, which was
granted access to the web resource). To further illustrate the point, you can try
accessing a servlet or JSP page in this way, which has a call to request.
getUserPrincipal( ). You will find that this call returns jon as you would expect.
So, here is a summary of what happens, as was illustrated in Figure 17-2:
The client attempts to access a protected web page. The web container notes that the client does not have any security credentials and that the web application implements identity assertion, so it fires up the Identity Assertion Providers, passing in the appropriate request parameters.
The Identity Asserter grabs the username directly from the incoming token and returns it in the form of a callback handler.
Any login modules that you have configured for the security realm then fire, using the callback handler to fetch the username. So, for example, the Default Authenticator will fire and log in the user. However, because it knows that the data comes from the Identity Asserter, it will not require a password. As a result, the user is logged in and can now access the web application.
Avinash Chugh, Jon Mountjoy presently works as Senior Development Manager for a firm that produces software for the regulated industries (finance, energy, pharmaceutics).
Return to ONJava.com.
Copyright © 2007 O'Reilly Media, Inc.