The J2EE platform architecture provides for the secure deployment of application components. It emphasizes the declarative approach wherein the application components' security structure, roles, access control, authentication and authorization requirements - as well as the other characteristics pertaining to transactions, persistence, and more - are expressed and managed outside the application code.
J2EE server products provide deployment tools that support the declarative customization of application components for the operational runtime environment. An application component provider isn't expected to implement security services, as it's the responsibility of the J2EE container and server. The security functions provided by the J2EE platform include authentication, access authorization, and secure communication with clients.
Authentication is the process by which an entity (such as a user, organization, or program) proves and establishes its identity with the system by supplying authentication data. For users this typically means a user name and password. (In general, authentication data could be comprised of a digital certificate, or even biometric data such as a fingerprint, iris, or retina scan.) A principal is an entity that can be validated by an authentication mechanism; it's identified using a principal name and verified using authentication data.
Authorization and Access Control
Authorization and access control mechanisms ensure that only authenticated principals who have the required permissions to access application components are able to do so. In general, there are two fundamental approaches to controlling access - capabilities and permissions. The capabilities-based approach focuses on what resources a given user can access. The permissions-based approach on the other hand focuses on which users can access a given resource. The J2EE authorization model uses role-based permissions. A role is a logical grouping of users that's used to define a logical security view for the application. Protected application resources have associated authorization rules - roles that are allowed to access a given component are specified in the application component's deployment descriptor. The deployer maps the roles to actual users using the J2EE server's deployment tools. The J2EE server enforces the prescribed security policies at runtime and ensures that only those users who belong to the appropriate role are able to access the protected components' functionality - while those who don't belong are denied access.
A container is part of the J2EE server. It provides deployment and runtime support for application components and is responsible for infrastructural services including security. There are three types of J2EE containers that are meant to house different J2EE application components:
Each of these containers has its associated deployment descriptor.
- EJB container: Houses EJB components
- Web container: Houses servlets, JSPs, static HTML, JPEG files, and more
- J2EE application client container: Houses J2EE application clients
Obtaining the Initiating Security Context
In the J2EE model, secure applications require that the client programs be authenticated. An end user can be authenticated using either a Web or an application client. Once the user is authenticated, an initial security context is generated and maintained by the J2EE platform. This security context is the encapsulation of the identity of the authenticated user principal.
Web Container's Support for Authentication
The <auth-method> element in the Web deployment descriptor is used to configure the type of authentication mechanism such as "BASIC", and "FORM".
The deployment tools provided with the J2EE server product insulate us from having to manually write the deployment descriptors; the elements are provided here for reference. The following are some of the authentication mechanisms made available by the Web container and server.
HTTP basic authentication is the simplest. When a user attempts to access any protected Web resource, the Web container checks if the user has already been authenticated. If the user hasn't, the browser's built-in login screen is used to solicit the user name and password from the user so that the Web server can perform authentication. If the login fails, the browser's built-in screens and messages will be used. The user name and password are sent using simple base 64 encoding.
Form-based authentication is used if an application-specific login screen is required, and the browser's built-in authentication screen isn't adequate. The Web deployment descriptor needs to specify the login form page and the error page to be used with this mechanism. When the user attempts to access any protected Web resources, the Web container checks if the user has already been authenticated. If the user hasn't, the container presents the login form as specified in the deployment descriptor. If authentication fails, the error page, as specified in the deployment descriptor, is displayed.
The <form-login-page> and <form-error-page> elements are respectively used to specify the location of the login and error pages that need to be displayed. Further, the login page must contain fields named precisely j_username and j_password to represent the user name and password, respectively, as shown in Listing 1.
HTTPS (HTTP over SSL) authentication is a strong authentication mechanism. It requires the user to possess a public key certificate and is ideal for e-commerce applications as well as single sign-ons from within the browser in an enterprise.
In both the HTTP basic and the form-based authentication, passwords aren't adequately protected for confidentiality. This deficiency can be overcome by running HTTP basic and HTTP form-based authentication mechanisms over SSL. Generally, the use of the CONFIDENTIAL flag in the <transport-guarantee> element of the Web deployment descriptor ensures the use of SSL for data transmission.
All J2EE specification-compliant Web servers support the concept of a single sign-on so that one login session can span multiple applications, thus allowing a user to log in once and access multiple applications.
J2EE Client Container's Support for Authentication
A J2EE application client is a Java application, but differs from a "plain" Java application client in that it's a J2EE component that's deployed in the J2EE "client" container. When a J2EE application client is run, a login window provided by the J2EE application server will pop up and require user name and password fields to be input. Once the user is authenticated, the application will be started. The authenticated user security context is obtained, maintained, and propagated by the J2EE platform so that the client application code doesn't have to deal with the login issue. As with other J2EE components, a J2EE application client is created with the J2EE server's deployment tools. J2EE application clients provide a standard and portable way for authentication, therefore the J2EE architecture encourages the use of J2EE application clients rather than "plain" Java application clients.
Authentication of 'Plain' Java Application Clients
A "plain" Java application client is a nonbrowser based, stand-alone Java application. There's no standard procedure defined by the EJB 1.1 specification regarding their authentication. However, several application servers support a procedure that involves appropriately setting the environmental property constants, java.naming.Context.SECURITY_PRINCIPAL and java.naming.Context.SECURITY_CREDENTIALS, and creating the initial context based on the same (see Listing 2).
Security Context and Access Control
Once the J2EE platform performs authentication and obtains the security context of an authenticated principal, it's maintained in the background. Whenever an attempt is made to access a protected application component, the container uses the roles known to be associated with the authenticated user in conjunction with the roles authorized to access the component, as prescribed in the deployment descriptor, to either permit or deny access. For example, the container will allow the authenticated user "John Doe," who belongs to the "sales representative" role, to access a protected component with a deployment descriptor setting that specifies "sales representative" as one of the roles authorized to access it.
Web Component Authorization
JSPs, servlets, and HTML pages can be set up as protected resources using J2EE server deployment tools. The <security-constraint> and its subelement <auth-constraint> are used respectively to associate security constraints on Web resources and indicate the user roles that should be permitted access to the Web component. The roles used here should appear in the <security-role-ref> element.
This security context can also be propagated from the Web to the EJB container, which will use it to authorize access to the EJB components' methods.
EJB Component Authorization
The EJB container authorizes access to EJB components based on the caller's security context, in conjunction with the permissions stipulated in the EJB deployment descriptor(ejb-jar.xml), via the <method-permission> and <role-name> elements.
The deployment descriptor setting in Listing 3 grants permissions to all the methods (indicated by the "*") exposed via the home and remote interfaces to an authenticated user in the role of "customer".
The deployment descriptor setting shown in Listing 4 grants specific permissions to the placeOrder() method exposed via the home and remote interfaces to an authenticated user in the role of "customer".
Propagating the Security Context
When one component calls another, the J2EE platform architecture provides for the propagation of the caller security context across components along a call chain. The security context can propagate across the various J2EE containers within the J2EE server. For example, it can propagate from the JSP component housed in the Web container to an EJB component housed in the EJB container. The J2EE platform's support for the propagation of the sensitive security context information provides reliability and convenience. It eliminates the need for the security context information to be passed around as an additional parameter in the business methods.
As such, the deployer can configure the identity selection policy for intercomponent calls so that a specified principal identity other than the original caller identity will be propagated down the call chain. The proposed EJB 2.0 specification provides a standard technique for addressing the issue of identity selection along a call chain via the <use-caller-identity> and <run-as-specified-identity> elements of the deployment descriptor. If <use-caller-identity> is specified for a component, it results in the propagation of the caller principal along the call chain. If the <run-as-specified-identity> element is used to specify a particular identity, then that specific principal identity is propagated down the call chain and all access control is governed by permissions for the specified identity.
Programmatically Querying the Security
The getRemoteUser(), getUserPrincipal(), and isUserInRole() methods available in the HttpServletRequest interface provide servlets and JSPs with access to security context information. The getRemoteUser() method obtains the name of the authenticated user, while the getUserPrincipal() returns the principal object associated with the authenticated user. These methods may be useful for recording the user's access to Web components or dynamically generating HTML content that includes the name of the user. Similarly, the isUserInRole( String roleName) queries the underlying security mechanism of the container to determine if the authenticated caller belongs to a given security role. This may be useful for dynamically generating Web content and options based on the role of the user.
Similarly, the EJBContext provides EJB components access into the security context (as well as transaction context). EJBContext provides two methods that allow programmatic access to security-related information, namely getCallerPrincipal() and isCallerInRole(). The getCallerPrincipal() allows the EJB component to obtain the principal object associated with the caller. This may be used for info only in conjunction with declarative management. In a common scenario, getCallerPrincipal() may be called from within an entity EJB component to facilitate saving the loginID of the user who caused the insertion of the row to the database. This can be accomplished by using code in the ejbCreate() method as below:
String loginID is a container-managed persistent field that represents the login/user name that caused the insertion of a particular row, and EntityContext theEntityContext was obtained using the callback setEntityContext(). This approach allows sensitive data for recording purposes to be obtained via the EJBContext.
The method isCallerInRole(String roleName) allows the bean implementation to query whether the caller belongs to a particular named role. Typically this is used to provide fine-grained control access and/or programming logic corresponding to that role. When programmatic calls are made to the isCallerInRole() method, the component provider declares the logical role names referenced in the code in the deployment descriptor, and the deployer is responsible for mapping the logical role to an actual security role. This is achieved via the <security-role>, <security-role-ref>, and <role-link> elements of the deployment descriptor.
Currently there's no standard mechanism for the propagation of the security context from the J2EE server to the enterprise information systems (EIS), such as database, ERP, or mainframe transaction processing systems. The J2EE Connector Architecture, which is expected to be included in the next version of the J2EE platform specifications, addresses the propagation of the security context from the application server to the EIS. The objective is to extend the end-to-end security model of J2EE applications to include the EIS tier.
The next release of the J2EE specifications is expected to include the Java Authentication and Authorization Service (JAAS), which implements the Pluggable Authentication Module (PAM) framework and endeavors to make the login services independent of the actual underlying authentication mechanism, so that different security and authentication implementations may be "plugged" in or out seamlessly.
More than one J2EE server product may be used in the production environment of a large enterprise; therefore the passing of the security context between different J2EE server products assumes significance. Currently, the mechanism of passing the security context tends to be specific to the particular J2EE server product. In the current J2EE specification, the EJB to CORBA mapping, which addresses the propagation of the security context over IIOP, is not a required feature. The next release of the J2EE platform specification is expected to make support for the CORBA/IIOP interoperability protocols mandatory, thereby facilitating the seamless passing of the security context between components from different vendors deployed on J2EE servers.
The J2EE architecture provides a powerful security model that's simple yet robust and reliable. It emphasizes the flexible and cost-effective declarative approach of managing security via XML-based deployment descriptors. It provides for the propagation of the security context across components along a call chain. This eliminates the need for the user information to be passed around as an additional parameter in the business method calls, thereby providing reliability and convenience. It also supports the concept to applications of single Web sign-on access.
Sanjay Mahapatra works for Cook Systems International. He's been writing distributed and object-oriented applications for more than five years and is a Sun-certified Java developer and Java 2 platform architect.
<form method="POST" action="j_security_check">
<input type="text" name="j_username">
<input type="password" name="j_password">
Properties props = new Properties () ;
props.put ( Context.SECURITY_PRINCIPAL, theUserLogin ) ;
props.put ( Context.SECURITY_CREDENTIALS, thePassword ) ;
IntialContext ic = new InitialContext ( props ) ;
// proceed with lookup using above InitialContext ic ...