Java EE Security API
The Java™ EE Security API 1.0 specification defines a set of security-related annotations that you can use for authentication. The specification also defines a common set of APIs that you can use for programmatic security.
The Java EE Security API 1.0 specification extends self-contained application security capabilities to port across different application servers and uses modern programming concepts, such as Expression Language (EL) and Contexts and Dependency Injection (CDI). The following list contains information that is specific to the Liberty profile to support the specification.
appSecurity-3.0
The appSecurity-3.0
feature provides the core capability for the Java EE Security API 1.0 specification (JSR 375 specification).
This feature includes the el-3.0
and cdi-2.0
features. If you want
to configure the DatabaseIdentityStore
annotation, configure one of the
jdbc-4.x
features. The javaee-8.0
and
webProfile-8.0
features include all the required features.
The appSecurity-3.0
feature supports the specification in the Liberty profile. This feature supports the
SecurityContext API and all of the annotations that are defined in the specification. To validate
your credentials and collect group information, the specification includes support for
authentication mechanisms that use annotations, such as BASIC, FORM, and CustomFORM, as well as
Lightweight Directory access Protocol (LDAP) and database identity stores. The
appSecurity-3.0
feature also specifies a common set of APIs in the SecurityContext
class to perform programmatic security checks. The Liberty profile provides CDI bean implementations
for all of the annotations in the specification. In addition, the applications can provide their own
custom CDI bean implementations for these annotations.
LDAPIdentityStore
You can use the password utilities capability from the Liberty profile to encrypt or encode the password for the bindDNPassword attribute. For more information on encrypting passwords with the securityUtility command, see securityUtility command.
DataBaseIdentityStore
The database identity store requires a data source configuration to provide the database
information. The dataSourceLookup
element on the
DatabaseIdentityStore
annotation references the data source that is defined in the
Liberty configuration, such as the
server.xml file, or in a Datasource
annotation. Also, define
the JDBC driver library for the database type in the Liberty configuration.
You can configure a data source in
multiple ways. For more information about defining an authentication alias on the data source, see
Configuring authentication aliases for Liberty. If the data source uses an authentication alias to
define the user and password for the datasource, the datasourceLookup parameter on the
DatabaseIdentityStore annotation must be an indirect JNDI lookup of the data source to find the
additional resources. For example, the indirect JNDI lookup that is defined on the datasourceLookup
parameter on the DatabaseIdentityStore annotation would be
java:comp/env/jdbc/MyDatasource
.
Authorization
Choose how to set up authorization for your application based on your requirements.
- Configure authorization to use the automatic role to group mapping:
- Do not configure any authorization table or role bindings in the
application-bnd
element in the ibm-application-bnd.xmi, ibm-application-bnd.xml, or server.xml files. - Make sure that the group name that is returned by the identity store is the same as the role
name.
- When you use the
LDAPIdentityStore
annotation, the group name corresponds to the groupNameAttribute attribute of the annotation. For example, assume that the following LDAP group object exists:
The LdapIdnetityStore annotation returns group1 as a group name when the value of the groupNameAttribute attribute is set to cn. Therefore, the role name must be configured to group1.cn=group1, o=mycompany, c=us
- When you use the
- Do not configure any authorization table or role bindings in the
- Specify a set of users or groups for authorization authority:
- Configure the role bindings in the
application-bnd
entry for that application. - You can configure in different ways based on the identity store configurations in the
server.When you use the LDAPIdentityStore annotation, the group name corresponds to the groupNameAttribute attribute of the annotation. This name must correspond to the access ID of the group attribute in the security role. For example, assume that the following group object exists:
The LdapIdnetityStore annotation returnscn=group1, o=mycompany, c=us
cn=group1, o=mycompany, c=us
as a group name when the value of the groupNameAttribute attribute is set to dn. Therefore, the access-id in the application-bnd element must be set to cn=group1, o=myCompany, c=us.
- Configure the role bindings in the
Refer to the following examples to configure role bindings in different ways:
- In the following examples, the access-id and name
fields are required. The complete format for the access-id field is
user:realmName/userUniqueID
for users andgroup:realmName/groupUniqueID
for groups. The user and groupuniqueIDs
correspond to the information that the application identity providers return.For example, when you use the
LDAPIdentityStore
annotation, theuserUniqueID
is the distinguished name (DN) for the user, and thegroupUniqueID
is based on thegroupNameAttribute
setting in theLDAPIdentityStore
annotation. These values also correspond to thegetCallerUniqueID
andgetCallerGroups
methods in theCredentialValidationResult
object that is returned by the validate method identity store. TherealmName
corresponds to theIdentityStoreID
of the identity store that is used. - If an application uses only one identity store, you can configure the
application-bnd
entry as shown in the following example. You do not need to provide therealmName
orIdentityStoreID
because only one identity store is configured:<application type="war" id="sample" name="sample" location="sample.war"> <application-bnd> <security-role name="role1"> <user name="bob" access-id="cn=Bob Smith, o=myCompany, c=us" /> <group name="group1" access-id="cn=group1, o=myCompany, c=us" /> </security-role> </application-bnd> </application>
- If an application uses multiple identity stores, configure the corresponding
realmName
orIdentityStoreID
where the user and group exist.In the following example,
ldapHostName:636
is theIdentityStoreID
returned by the LDAP identity store. When you use the database identity store, theIdentityStoreID
is thedataSourceLookup
name that is used for the database, which is the full JNDI name of the data source. TherealmName
andIdentityStoreID
are used to distinguish users and groups across different identity stores. When you use your custom identity stores, make the values match the corresponding identity store identifiers.- LDAP identity store
example:
<application type="war" id="sample" name="sample" location="sample.war"> <application-bnd> <security-role name="role1"> <user name="bob" access-id="ldapHostName:636/cn=Bob Smith, o=myCompany, c=us" /> <group name="group1" access-id="ldapHostName:636/cn=group1,o=myCompany,c=us" /> </security-role> </application-bnd> </application>
- Database identity store
example:
<application type="war" id="sample" name="sample" location="sample.war"> <application-bnd> <security-role name="role1"> <user name="bob" access-id="jdbc/myDataSource/fullNameofBob" /> <group name="group1" access-id="jdbc/myDataSource/fullGroupNameofGroup1" /> </security-role> </application-bnd> </application>
- LDAP identity store
example:
- In addition, the server supports the current extended format to specify
access-id
for the user and the group with therealmName
anduniqueID
information. Use the extended format to explicitly configure the completeaccess-id
file.- LDAP identity store
example:
<application type="war" id="sample" name="sample" location="sample.war"> <application-bnd> <security-role name="role1"> <user name="bob" access-id="user:ldapHostName:636/cn=Bob Smith, o=myCompany, c=us" /> <group name="group1" access-id="group:ldapHostName:636/cn=group1, o=myCompany, c=us" /> </security-role> </application-bnd> </application>
- Database identity store
example:
<application type="war" id="sample" name="sample" location="sample.war"> <application-bnd> <security-role name="role1"> <user name="bob" access-id="user:jdbc/myDataSource/fullNameofBob" /> <group name="group1" access-id="group:jdbc/myDataSource/fullGroupNameofGroup1" /> </security-role> </application-bnd> </application>
- LDAP identity store
example:
EAR file
- If any EJB modules are part of the application, use the user registry configuration in the server.xml file for all the modules in that application instead of using identity stores.
- If identity stores need to be configured for the web modules, use the same identity stores for all the web modules. If the EAR file also contains EJB modules, the user registry that is configured in the server.xml file must match the identity stores configured. The information that must match includes the user and group, and the realm name of the user registry to the identity store ID.
- If the modules communicate with each other, configure the complete access IDs as specified in
the authorization section when one of these conditions occurs:
- The modules in the application contain different identity stores.
- The user registry in the server.xml file does not match the identity stores.
Override the application provided authentication method or mechanism.
- Configure the override to use the basic authentication
mechanism.
If the basicAuthenticationMechanismRealmName attribute is not specified, the value of defaultRealm is used.<webAppSecurity overrideHttpAuthMethod="BASIC" basicAuthenticationMechanismRealmName="myRealm" />
- Configure the override to use the form authentication mechanism.Specify both the login form page and the login error page. These two pages must be part of a separate WAR file.
- Specify the context root for the login pages by using the
contextRootForFormAuthenticationMechanism
element.
<webAppSecurity loginFormURL="/global/login/globalLogin.jsp" loginErrorURL="/global/login/globalLoginError.jsp" overrideHttpAuthMethod="FORM" contextRootForFormAuthenticationMechanism="/global/login" /> <application type="war" context-root="/global/login" id="globalLogin" name="globalLogin" location="globalLogin.war"/>
- Specify the context root for the login pages by using the
contextRootForFormAuthenticationMechanism
element.
- Configure the override to use the client certificate.You can use a client certificate for authentication of all the applications by setting the overideHttpAuthMethod method to CLIENT_CERT:
<webAppSecurity overrideHttpAuthMethod="CLIENT_CERT"/>
If the client certificate authentication fails, you can configure the failover to a different authentication method by specifying the allowAuthenticationFailOverToAuthMethod element. The following values are acceptable for the allowAuthenticationFailOverToAuthMethod element:- BASIC: Basic Authentication is used.Example of a failover to the basic login:
<webAppSecurity overrideHttpAuthMethod="CLIENT_CERT" allowAuthenticationFailOverToAuthMethod="BASIC"/>
- FORM: Form login is used. When the value is set to FORM, make
sure that the loginFormURL and loginErrorURL attributes are set.
Example of a failover to the form login:
Specify the loginForm and loginError URLs and the .war file that packages those files:<webAppSecurity overrideHttpAuthMethod="CLIENT_CERT" allowAuthenticationFailOverToAuthMethod="FORM" loginFormURL="globalLogin/globalLogin.jsp" loginErrorURL="globalLogin/globalLoginError.jsp"/> <application type="war" id="globalLogin" name="globalLogin" location="globalLogin.war"/>
- APP_DEFINED: The login method that is used is the one that the application is
configured with in either the web.xml file or the annotation.
Example of a failover to the authentication method defined in the application by using the APP_DEFINED value:
<webAppSecurity overrideHttpAuthMethod="CLIENT_CERT" allowAuthenticationFailOverToAuthMethod="APP_DEFINED"/>
When you use client authentication, set the clientAuthentication or clientAuthenticationSupported attribute of the SSL element to true so that the client sends over the certificate.
- BASIC: Basic Authentication is used.
Things to consider when you implement a custom HTTP authentication mechanism or an identity store.
- Annotate your custom HttpAuthenticationMechanism interface so that it uses the RememberMeIdentityStore interface that you implemented. By implementing the custom HttpAuthenticationMechanism interface, you have single sign-on for your environment so that each subsequent login for a user can use the information in the RememberMe cookie and avoid the need to log in every time.
- Do not use both the RememberMe cookie and the AutoApplySession annotation as they serve similar purposes, and using both can result in inconsistent authorization behavior.
- If you provided your own IdentityStore implementation that sets the ID in the CredentialValidationResult class and annotated your custom HttpAuthenticationMechanism implementation with the RememberMe annotation, then ensure that the realm name is not set in the access-id field of the role binding. You want to ensure that the realm name is not set this way because the IdentityStore ID is used as the realm for authorization, but when the RememberMe cookie is processed, the IdentityStore ID is not retained.
- Create the CredentialValidationResult class with the IdentityStore ID so that the ID can be used as the realm name.
Implementing a custom password hash for a database identity store
A default PasswordHash implementation is provided for an identity store using a database. A PasswordHash is required to validate user passwords in the database. To provide a custom implementation for the PasswordHash, implement javax.security.enterprise.identitystore.PasswordHash, and make it available as a CDI bean in a shared library.
- Develop a javax.security.enterprise.identitystore.PasswordHash implementation. The following
example shows a sample dummy password
hash:
package com.ibm.ws.security.pwdhash.test; import javax.security.enterprise.identitystore.PasswordHash; /** * Test PasswordHash sample for DatabaseIdentityStore. For testing purposes only. */ public class TestHash implements PasswordHash { public TestHash() { System.out.println("Init TestHash"); } @Override public boolean verify(char[] incomingPwd, String existingPwd) { System.out.println("TestHash is for testing purposes only."); String pwd = String.valueOf(incomingPwd) + "_DUMMY_HASH"; if (pwd.equals(existingPwd)) { return true; } return false; } @Override public String generate(char[] pwdToHash) { return String.valueOf(pwdToHash) + "_DUMMY_HASH"; } }
- Package the PasswordHash implementation in a jar and make the PasswordHash class available as a
CDI bean. There are different ways to mark the class as a bean.
- One option is to add a beans.xml file with
bean-discovery-mode=all to the META-INF folder of the jar.
The following is an example beans.xml
file
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/beans_1_2.xsd" bean-discovery-mode="all" version="1.1"> </beans>
- A different option is to add the @dependent annotation to the PasswordHash
class
package com.ibm.ws.security.pwdhash.test; import javax.enterprise.context.Dependent; import javax.security.enterprise.identitystore.PasswordHash; /** * Test PasswordHash sample for DatabaseIdentityStore. For testing purposes only. */ @Dependent public class TestHash implements PasswordHash { ...
- One option is to add a beans.xml file with
bean-discovery-mode=all to the META-INF folder of the jar.
The following is an example beans.xml
file
- Add the PasswordHash implementation jar as a library element to the
server.xml, where dir attribute points to the location of
the JAR file. The following is an example of adding a library
element
<library id="CustomHashLib" fat.modify="true"> <fileset dir="${server.config.dir}" includes="CustomPasswordHash.jar"/> </library>
- Add the commonLibraryRef reference to the database IdentityStore application
in the server.xml. The following is an example of adding a classloader element
to an
application.
<application type="war" id="DatabaseAnnotatedCustomHashServlet" name="DatabaseAnnotatedCustomHashServlet" location="DatabaseAnnotatedCustomHash.war"> <application-bnd> <security-role name="javaeesec_basic"> <user name="blue1" access-id="user:java:comp/DefaultDataSource/blue1" /> </application-bnd> ... <classloader commonLibraryRef="CustomHashLib" /> </application>
- The hashAlgorithm property on the DatabaseIdentityStoreDefinition annotation
should match the class that is provided in the jar. The following is an example of the
DatabaseIdentityStoreDefinition
annotation
@DatabaseIdentityStoreDefinition( callerQuery = "select password from callers where name = ?", groupsQuery = "select group_name from caller_groups where caller_name = ?", hashAlgorithm = com.ibm.ws.security.pwdhash.test.TestHash.class)
- Use the same custom hash algorithm to generate passwords that are stored in the database accessed by the database IdentityStore.
Encrypting the password for database identity store users using the default password hash provider
Instance<? extends PasswordHash> instance = CDI.current().select(Pbkdf2PasswordHash.class);
PasswordHash passwordHash = instance.get();
passwordHash.initialize(..)
password.generate(...)
Use LTPA Token for Single Sign on (SSO)
The Java EE Security API 1.0 (JSR 375) supports the RememberMeIdentityStore interface. A JSR 375 provider can implement this interface so that the user information is remembered after initial authentication. The cookie that is generated by the RememberMeIdentityStore implementation can be used to achieve SSO without having to prompt the user again until it expires. This capability gives more flexibility for the provider to achieve SSO and is the required when you have different identity stores configured in your applications.
In addition to the specification defined RememberMeIdentityStore interface, you can also use the LTPA token in the LTPA cookie that is generated by the Liberty profile to achieve SSO. Be aware of the following information when you use the LTPA cookie in a JSR 375 application.
- When you use the JSR 375 specification, additional information is added to the LTPA token in the LTPA cookie. When this LTPA token is sent back again or to another server in the same SSO domain, it is validated. If the validation succeeds, the HttpAuthenticationMechanism implementation, or a JASPIC provider if it is configured instead, is called to complete the authentication.
- If the SSO is only among JSR 375 enabled applications and only the default HttpAuthMechanism implementations are used, the LTPA token is validated. If the validation is successful, the user is not prompted again to log in. Validation of the token requires that the LTPA keys are exchanged and that the user registry is the same, or that the same identity stores are configured in all the applications. If validation fails, the user is prompted to log in based on the HttpAuthMechanism implementation configured in the application. For more information on LTPA token-based SSO that is based on a token, see Customizing SSO configuration using LTPA cookies in Liberty.
- If an LTPA cookie from a non-JSR 375 application is presented to a JSR 375 enabled application, the HttpAuthenticationMechanism implementation is called even after the token is validated. This call is done according to the specification requirements. This requirement also holds true when a JASPIC provider is configured. The HttpAuthMechanism implementation then prompts the user again.
- One might not want to be prompted as described in the previous list item. For instance, you
might want to use the validated LTPA token to authenticate the user without calling the
HttpAuthenticationMechanism implementation or a JASPIC provider. In this situation, set the
following property in the server.xml
file:
<webAppSecurity useLtpaSSOForJaspic="true" />
- When this property is set and a valid LTPA token is presented, the token is used to authenticate the user without calling the HttpAuthenticationMechanism implementation or the JASPIC provider. Set this flag to true only if the user registry is the same across all the servers that are participating in SSO. If any identity stores are specified in the code, do not set the flag as the identitystores implementation might not be the same in all the applications in all the servers. A typical scenario where this flag is set would be where only the HttpAuthMechanism annotation is specified in the code without any identity stores specified. In this case, the user registry that is configured in the server.xml file is used.
- If you want a specific LTPA cookie for a server, you can set up a specific custom LTPA cookie
name for each server by setting the following ssoCookieName attribute. In this case, only the
specific LTPA cookie that is configured is generated and verified by that server. Any other LTPA
cookies that are generated by other servers are not
used.
<webAppSecurity useOnlyCustomCookieName="true", ssoCookieName="myServerXCookieName" /> ------> Change the cookieName and make it unique across all the servers
- If the LTPA cookie that is generated from a JSR 375 server or a JASPIC server is presented to a non-JSR 375 server or a non-JASPIC server, it is validated and used for SSO without prompting the user.