JBossSX uses JAAS to integrate application servers and security infrastructures A key aspect of the Java 2 Platform, Enterprise Edition (J2EE) component models is a simple declarative security model. The Enterprise JavaBean (EJB) 2.0 and Servlet 2.2 specifications support a role-based declarative security model that externalizes security from application logic and decouples the application security roles from the deployment environment’s security implementation. At the application level, the ejb-jar.xml and web.xml deployment descriptors define security. Although this model allows for an independent, simple specification of the application server’s security requirements, mapping the application-defined security onto the deployment environment security’s infrastructure is an application-server-specific activity. Thus, configuring a J2EE application’s security requires proprietary application server APIs or tools. One such tool is the Java Authentication and Authorization Service (JAAS). In this article, I describe how the JBoss security extension, JBossSX, uses the standard JAAS to integrate with the deployment environment’s security infrastructure. By configuring the JAAS login modules bundled with JBoss, you can complete the integration without custom programming. If the bundled login modules don’t work with your security infrastructure, you can simply write a custom login module that does; I’ll show you how in this article. The key topics that I cover here include: The J2EE declarative security modelThe key JAAS classesDetails of how the JBossSX security manager uses JAAS in its implementation of the J2EE declarative security modelDetails of how you can write a custom JAAS login module for JBossI also include a secure enterprise application example that demonstrates the declarative security model and the configuration of JAAS login modules to integrate the deployment environment’s security. J2EE declarative security overviewThe first step to securing a J2EE application is to specify the application security requirements via the standard J2EE deployment descriptors. You secure access to EJBs and Web components in an enterprise application by using the ejb-jar.xml and web.xml deployment descriptors. Figures 1 and 2 illustrate the security-related elements in the EJB 2.0 and Servlet 2.2 deployment descriptors, respectively. Together, these security elements define the bean author and application assembler’s view of an enterprise application’s security requirements. So that you’ll have sufficient background for the example presented later in this article, I’ll first review the security elements pictured in Figures 1 and 2. Enterprise beans security referencesAs you can see in Figure 1, enterprise beans may declare one or more security-role-ref elements. An EJB can access the caller principal and ask if the caller belongs to a role by name. The caller principal is obtained from the EJBContext.getCallerPrincipal() method as a java.security.Principal instance. Using the EJBContext.isCallerInRole(String) method, an EJB checks if a caller is in a role that has been declared with a security-role-ref element. The role-name element value must link to a security role in the assembly-descriptor section of ejb-jar.xml through the role-link element. You typically use isCallerInRole() to perform a security check that cannot be defined using method permissions. See section 21.2.5 of the EJB 2.0 Specification PFD2 for more details on accessing the caller’s security context. Enterprise beans security identityFigure 1 also shows that enterprise beans can optionally declare a security-identity element. New to EJB 2.0 is the ability to specify what identity an EJB should use when it invokes methods on other EJBs. The application assembler uses the security-identity element to indicate that the current caller’s identity should be propagated by using a use-caller-identity element as security-identity‘s value. Alternatively, the application assembler can use the run-as element with the security-identity‘s value as role-name to specify that EJB calls are performed with the security role given by the role-name value. Note that this does not change the caller’s identity as seen by EJBContext.getCallerPrincipal(). Rather, the caller’s security roles are set to the single role specified by the run-as/role-name element value. You can use a run-as identity to keep external clients from accessing internal EJBs. To do that, assign the internal EJB method permissions that restrict access to a role never assigned to an external client, and use the restricted role as the run-as/role-name element value for EJBs that use the internal EJB. Assembly descriptor security rolesThe security role referenced by either security-role-ref or security-identity elements needs to map to one of the application’s declared roles. An application assembler defines logical security roles by adding security-role elements to the assembly-descriptor element. In JBoss, a security-role is only used to map an EJB security-role-ref/role-name to the logical role to which the EJB role name refers. The user’s assigned roles are a dynamic function of the application’s security manager, as you will see when I discuss the JBossSX implementation. JBoss does not require defined security-roles to identify method permissions. Therefore, you should specify a security-role element for every role used in the method-permission element for portability across application servers and for deployment descriptor maintenance. Assembly descriptor method permissionsAn application assembler can set the roles that are allowed to invoke an EJB’s home and component interface methods through method-permission elements. Each method-permission element contains one or more role-name elements that define the logical roles allowed access to one or more EJB methods as identified by method elements. With EJB 2.0, you can now specify the unchecked element instead of the role-name element to declare that an authenticated user can access one or more methods. In addition, you can declare that no one should have access to a method with the exclude-list element. For method syntax, see section 21.3.2 of the EJB 2.0 Specification PFD2. An example EJB deployment descriptorThe following ejb-jar.xml descriptor illustrates the use of EJB security elements and is the descriptor used in the article example: <ejb-jar> <display-name>SecurityTests</display-name> <enterprise-beans> <session> <description>A trivial stateless session echo bean</description> <ejb-name>PublicSession</ejb-name> <home>org.jboss.docs.jaas.howto.SessionHome</home> <remote>org.jboss.docs.jaas.howto.Session</remote> <ejb-class>org.jboss.docs.jaas.howto.StatelessSessionBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> <ejb-ref> <ejb-ref-name>ejb/PrivateSession</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home>org.jboss.docs.jaas.howto.SessionHome</home> <remote>org.jboss.docs.jaas.howto.Session</remote> <ejb-link>PrivateSession</ejb-link> </ejb-ref> <security-identity> <run-as> <role-name>InternalUser</role-name> </run-as> </security-identity> </session> <session> <description>A trivial stateful session echo bean</description> <ejb-name>PrivateSession</ejb-name> <home>org.jboss.docs.jaas.howto.SessionHome</home> <remote>org.jboss.docs.jaas.howto.Session</remote> <ejb-class>org.jboss.docs.jaas.howto.StatefulSessionBean</ejb-class> <session-type>Stateful</session-type> <transaction-type>Container</transaction-type> </session> </enterprise-beans> <assembly-descriptor> <security-role> <role-name>Coder</role-name> </security-role> <security-role> <role-name>Echo</role-name> </security-role> <security-role> <role-name>InternalUser</role-name> </security-role> <method-permission> <role-name>Echo</role-name> <method> <ejb-name>PublicSession</ejb-name> <method-name>*</method-name> </method> </method-permission> <method-permission> <role-name>InternalUser</role-name> <method> <ejb-name>PrivateSession</ejb-name> <method-name>*</method-name> </method> </method-permission> <method-permission> <role-name>Coder</role-name> <method> <ejb-name>PublicSession</ejb-name> <method-name>create</method-name> </method> <method> <ejb-name>PublicSession</ejb-name> <method-name>remove</method-name> </method> </method-permission> <method-permission> <unchecked/> <method> <ejb-name>PublicSession</ejb-name> <method-name>noop</method-name> </method> </method-permission> <exclude-list> <description>Methods that cannot be used in this deployment</description> <method> <ejb-name>PublicSession</ejb-name> <method-name>restricted</method-name> </method> </exclude-list> </assembly-descriptor> </ejb-jar> Web application security constraintsIn a Web application, security is defined by the roles allowed access to content; a URL pattern identifies the protected content. For example, the web.xml descriptor fragment below indicates that any URL lying under the Web application’s /restricted path requires an AuthorizedUser role: <web-app> ... <security-constraint> <web-resource-collection> <web-resource-name>Secure Content</web-resource-name> <url-pattern>/restricted/*</ url-pattern></ <web-resource-collection> <auth-constraint> <role-name>AuthorizedUser</role-name> </auth-constraint> </security-constraint> ... <security-role> <description>The role required to access restricted content</description> <role-name>AuthorizedUser</role-name> </security-role> </web-app> The security-role-ref and security-role elements are equivalent to their EJB counterparts. Specify the security domain in JBossThe J2EE security elements I have covered describe only the security requirements from the application’s perspective. Since J2EE security elements declare logical roles, the application deployer maps the roles from the application domain onto the deployment environment. The EJB specification omits these application-server-specific details. In JBoss, mapping the application roles onto the deployment environment entails specifying a security manager that implements the J2EE security model. Figure 3 shows the JBoss-specific EJB and Web application deployment descriptor’s security-related elements. Use the security-domain element to identify the Java Naming and Directory Interface (JNDI) name of the security manager interface implementation that JBoss uses for the EJB and Web containers. You may specify security-domain as an EJB-level element to only set security for one or more EJBs when security-domain is not a top-level element, or when security-domain is a top-level element and you want to override it. The security-proxy element identifies a custom security interceptor that allows per-request security checks outside the scope of the EJB declarative security model without embedding security logic into the EJB implementation. I won’t go into detail about that JBoss-specific feature, as this article focuses on using JAAS to implement the standard declarative security model. A JBoss security manager implementation requires the org.jboss.security.EJBSecurityManager and org.jboss.security.RealmMapping interfaces presented in Figure 4. Figure 4. JBoss security manager interfaces Click on thumbnail to view full-size image.For the remainder of this article, I focus on the JBossSX org.jboss.security.plugins.JaasSecurityManager, a JAAS-based implementation of the security manager interfaces. But first, I’ll present a brief introduction to JAAS.What is JAAS?The JAAS 1.0 API consists of a set of Java packages for the user authentication and authorization. It implements a Java version of the standard Pluggable Authentication Module (PAM) framework and compatibly extends the Java 2 Platform’s access control architecture to support user-based authorization. JAAS was first released as an extension package for JDK 1.3 and is bundled with the current JDK 1.4 beta. As the JBossSX framework only uses the authentication aspect of JAAS to implement the declarative-role-based J2EE security model, this introduction focuses only on that topic. For a more comprehensive overview, see the JAAS material in Resources. I took much of this section’s material from the JAAS 1.0 Developers Guide. JAAS authentication is performed in a pluggable fashion. This permits Java applications to remain independent from underlying authentication technologies and allows the JBossSX security manager to work in different security infrastructures. Integration with a security infrastructure can succeed without changing the JBossSX security manager. Authentication consists of instantiating a LoginContext object, which passes the name of a Configuration entry to determine which LoginModule will be created. The LoginModules define the authentication technology. LoginModules often use a username and password to verify identity, but any form of identity and identity verification will work. The JAAS core classesThe JAAS core classes break down into three categories: common, authentication, and authorization. The list below presents the common and authentication classes, since the JBossSX functionality covered in this article uses only these classes. Common classes: Subject (javax.security.auth.Subject)Principal (java.security.Principal)Authentication classes: Callback (javax.security.auth.callback.Callback)CallbackHandler (javax.security.auth.callback.CallbackHandler)Configuration (javax.security.auth.login.Configuration)LoginContext (javax.security.auth.login.LoginContext)LoginModule (javax.security.auth.spi.LoginModule)Subject and PrincipalTo authorize access to resources, applications first need to authenticate the request’s source. The JAAS framework defines the term Subject to represent the request’s source. Subject is the key JAAS class; it represents information for a single entity, such as a person or service. It encompasses the entity’s principals, public credentials, and private credentials. The JAAS APIs use the existing Java 2 java.security.Principal interface to represent a principal. Once authenticated, a Subject is populated with associated identities, or Principals. A Subject may have many Principals. For example, a person may have a name Principal (John Doe) and a social security number Principal (123-45-6789), both of which help distinguish it from other Subjects. To retrieve the Principals associated with a Subject, two methods are available: public Set getPrincipals(); public Set getPrincipals(Class c); The first method returns all Principals contained in the Subject. The second method only returns those Principals that are instances of Class c or Class c‘s subclasses. An empty set will return if the Subject has no associated Principals. Note that the java.security.acl.Group interface is a subinterface of java.security.Principal, and so an instance in the Principals set may represent a logical grouping of other principals or groups of principals. Authentication classesThe following steps will authenticate a Subject: An application instantiates a LoginContext.The LoginContext consults a Configuration to load all the LoginModules configured for that application.The application invokes the LoginContext‘s login method.The login method invokes all the loaded LoginModules. Each LoginModule attempts to authenticate the Subject. Upon success, LoginModules associate relevant Principals and credentials with the Subject.The LoginContext returns the authentication status to the application.If authentication succeeds, the application retrieves the authenticated Subject from the LoginContext.The LoginContext class provides the basic methods for authenticating Subjects and offers a way to develop an application independent of the underlying authentication technology. The LoginContext consults a Configuration to determine the authentication services — or LoginModules — configured for a particular application. Therefore, you can plug in different LoginModules under an application without changing the application itself. All LoginContext constructors share a common parameter: name. The LoginContext uses name to index the login Configuration. Actual authentication occurs with a call to the following method: public void login() throws LoginException; When login() invokes, all the configured LoginModules’ respective login methods invoke to perform authentication. If authentication succeeds, the following method can retrieve the authenticated Subject (which may now hold Principals, public credentials, and private credentials): public Subject getSubject(); To log out a Subject and remove its authenticated Principals and credentials, use the method below: public void logout() throws LoginException; The following code sample demonstrates the calls necessary to authenticate a Subject using the LoginModules configured under the name jwdomain: // let the LoginContext instantiate a new Subject LoginContext lc = new LoginContext("jwdomain"); try { // authenticate the Subject lc.login(); System.out.println("authentication successful"); // get the authenticated Subject Subject subject = lc.getSubject(); ... // all finished -- logout lc.logout(); } catch (LoginException le) { System.out.println("authentication unsuccessful"); le.printStackTrace(); } With the LoginModule interface, developers can implement different authentication technologies that can be plugged under an application. For example, one LoginModule may perform username/password-based authentication, while others may interface to hardware devices such as smart card readers or biometric authenticators. To instantiate a LoginModule, a LoginContext expects each LoginModule to provide a public constructor that takes no arguments. Then, to initialize a LoginModule with the relevant information, a LoginContext calls the LoginModule‘s initialize() method. The subject argument is guaranteed to be nonnull. The signature of the initialize() method is: void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options); The login() method starts authentication; its signature is: boolean login() throws LoginException; An example method implementation might prompt the user for a username and password, and then verify the information against data stored in a naming service such as NIS or LDAP. Alternative implementations might interface to smart cards and biometric devices or simply extract user information from the underlying operating system. The validation of user identity by each LoginModule is considered Phase 1 of JAAS authentication. Authentication completes when the LoginContext calls: boolean commit() throws LoginException; If Phase 1 succeeds, then the commit() method continues with Phase 2: associating relevant Principals, public credentials, or private credentials with the Subject. If Phase 1 fails, then boolean commit() throws LoginException; removes any previously stored authentication state, such as usernames or passwords. The following method halts authentication: boolean abort() throws LoginException; Typical implementations of that method also clean up previously stored authentication state. The following method logs out a Subject: boolean logout() throws LoginException; That method removes the Principals and credentials originally associated with the Subject during the commit operation. Credentials should be destroyed upon removal. When a LoginModule must communicate with the user to obtain authentication information, it uses a CallbackHandler. Applications implement the CallbackHandler interface and pass it to the LoginContext, which forwards it directly to the underlying LoginModules. LoginModules use the CallbackHandler both to gather input from users (such as a password or smart-card PIN number) and to supply information to users (such as status information). By allowing the application to specify the CallbackHandler, underlying LoginModules remain independent from the different ways applications interact with users. For example, a CallbackHandler‘s implementation for a GUI application might display a window to solicit user input. On the other hand, a CallbackHandler‘s implementation for a non-GUI environment, such as an application server, might simply obtain credential information using an application server API. The CallbackHandler interface has one method to implement: void handle(Callback[] callbacks) throws java.io.IOException, UnsupportedCallbackException; The last authentication class I will cover is the Callback interface. It is a tagging interface that features several implementations, including NameCallback and PasswordCallback. LoginModules use a Callback to request information required by the authentication mechanism the LoginModule encapsulates. LoginModules pass an array of Callbacks directly to the CallbackHandler‘s handle method during the authentication’s login phase. Inside the JBossSX JaasSecurityManagerNow let’s return to the JBossSX security manager implementation. I’ll start with an overview of how the JBoss security components interact. Figure 5 shows the main components and how the security domain assigned in the JBoss EJB and Web deployment descriptors ties them together. Figure 5 depicts an enterprise application that contains both EJBs and Web content secured under the security domain jwdomain. The EJB and Web containers have a request interceptor architecture that includes a security interceptor, which enforces the container security model. At deployment time, the security domain in the jboss.xml and jboss-web.xml descriptors is used to obtain the security manager instance associated with the container and used by the security interceptor. When a secured component is requested, the security interceptor delegates security checks to the security manager instance associated with the container. For the JBossSX default security manager implementation, shown in Figure 5 as the JaasSecurityMgr component, security checks are based on the information associated with the javax.security.auth.Subject instance. That instance results from executing the JAAS login modules configured under the name matching the security-domain element value. We will drill into the JaasSecurityManager implementation and its use of JAAS in the following sections. The JAAS in JaasSecurityManagerThe default JBossSX security manager implementation org.jboss.security.plugins.JaasSecurityManager uses the JAAS packages to implement its behavior. In particular, its behavior derives from the login module (javax.security.auth.spi.LoginModule) whose configuration entry name matches the security domain to which the JaasSecurityManager has been assigned. The login module implements the security domain’s principal authentication and role-mapping behavior. Hence, you can use the JaasSecurityManager across different security domains simply by plugging in different login module configurations for the domains. The security checkConsider a client that invokes a method on an EJB configured to use a JaasSecurityManager instance for security. Figure 6 illustrates some components involved in the security check; the security check’s key steps are outlined below the figure. The client first has to perform a JAAS login to establish the principal and credentials for authentication, which is how clients establish their login identities in JBoss. Support for presenting the login information via the JNDI InitialContext properties is not provided. A JAAS login entails creating a javax.security.auth.login.LoginContext instance and passing the configuration’s name to use. In Figure 6, the configuration name is other. This one-time login associates the login principal and credentials with all subsequent EJB method invocations. Note that the process might not authenticate the user. The nature of the client-side login depends on the login module configuration that the client uses. In Figure 6, the other client-side login configuration entry is set up to use the ClientLoginModule (org.jboss.security.ClientLoginModule); that module does not perform client-side authentication, but simply binds the username and password to the JBoss EJB invocation layer for later authentication on the server.Later, the client obtains an EJB’s home interface and attempts to create a bean, resulting in a home interface method invocation sent to the JBoss server. The invocation includes the method arguments passed by the client along with the user identity and credentials from the client-side JAAS login.On the server side, the security check first requires authentication of the user invoking the call, which, as on the client side, involves a JAAS login. The security domain under which the EJB is secured determines the choice of login modules. The security domain’s name is used as the login configuration entry name passed to the LoginContext constructor. In Figure 6, the EJB security domain is jwdomain. If the JAAS login authenticates the user, a JAAS Subject is created that contains the following in its PrincipalsSet: A java.security.Principal that corresponds to the client identity.A java.security.acl.Group named Roles that contains the role names from the application domain to which the user has been assigned. org.jboss.security.SimplePrincipal objects are used to represent the role names; SimplePrincipal is a simple string-based implementation of Principal. These roles are used to validate the roles assigned to methods in ejb-jar.xml and the EJBContext.isCallerInRole(String) method.An optional java.security.acl.Group named CallerPrincipal, which contains a single org.jboss.security.SimplePrincipal that corresponds to the identity of the application domain’s caller. The CallerPrincipal sole group member is the value returned by the EJBContext.getCallerPrincipal() method. The purpose of this mapping is to allow a Principal known in the deployment security environment to map to a Principal with a name known to the application. In the absence of a CallerPrincipal mapping the deployment security environment, Principal returns.The final step of the security check is to verify the authenticated user has permission to invoke the requested method. Doing this requires the following steps: Obtain the names of the roles allowed to access the EJB method from the EJB container. The names are ejb-jar.xml descriptor role-name elements of all method-permission elements containing the invoked method.If no roles have been assigned, then access to the method is denied. Otherwise, the JaasSecurityManager.doesUserHaveRole(Principal, Set) method invokes to see if the caller principal has one of the assigned role names. The method iterates through the role names and checks if the user javax.security.auth.Subject‘s Roles group contains a SimplePrincipal with the assigned role name. Access is allowed if any role name is a member of the Roles group; access is denied if none of the role names are members.If the EJB was configured with a custom security proxy, the method invocation is delegated to it. If the security proxy wants to deny access to the caller, it will throw a SecurityException. If no SecurityException is thrown, access to the EJB method is allowed; the SecurityInterceptor passes the method invocation to the next container interceptor.Note that you can configure the JaasSecurityManager to use a cache of authentication information so that a JAAS login is not performed on every method invocation. If no cache is specified in the JaasSecurityManager‘s configuration, a timed cache is used by default. JBossSX custom login modulesAs shown above, the JBossSX security manager relies on the JAAS login modules for a security domain for authentication and authorization. The security manager authorizes users with the information stored in the Subject. If the LoginModules that ship with the JBoss server do not integrate into your existing security infrastructure, you can write a custom LoginModule that does. Writing a custom login module entails mapping your security infrastructure information onto a javax.security.auth.Subject using the usage pattern expected by JBossSX. JBossSX Subject usage patternsThe JBossSX security manager inside the JBoss application server executes the login modules used by a security domain. Since these modules run inside the JBoss server, they are called server-side login modules. When you write a custom LoginModule, you write a server-side login module. To understand how server-side login modules are used in JBoss, you need to understand the JAAS Subject class’s information storage features. You can obtain security information associated with a Subject in six ways: java.util.Set getPrincipals()java.util.Set getPrincipals(java.lang.Class c)java.util.Set getPrivateCredentials()java.util.Set getPrivateCredentials(java.lang.Class c)java.util.Set getPublicCredentials()java.util.Set getPublicCredentials(java.lang.Class c)For Subject identities and roles, JBossSX has selected the most natural choice: the PrincipalsSet obtained via getPrincipals() and getPrincipals(java.lang.Class). The usage pattern follows: User identities (username, social security number, employee ID, and so on) are stored as java.security.Principal objects in the Subject Principals set. The assigned user roles are also stored in the Principals set, but in named role sets using java.security.acl.Group instances. The Group interface — a collection of Principals and/or Groups — is a subinterface of java.security.Principal. Any number of role sets can be assigned to a Subject. Currently, the JBossSX framework uses two well-known role sets: Roles and CallerPrincipal. The Roles set is the Principal set for the named roles as known in the application domain under which the Subject has been authenticated. This role set is used by methods like the EJBContext.isCallerInRole(String), which EJBs use to see if the current caller belongs to the named application domain role. The security interceptor logic that performs method permission checks also uses the role set. The CallerPrincipal role set consists of the single Principal identity assigned to the user in the application domain. The EJBContext.getCallerPrincipal() method uses CallerPrincipal to allow the application domain to map from the operation environment identity to a user identity suitable for the application. If a Subject does not have a CallerPrincipal role set, the application identity is the operational environment identity. Support for the Subject usage patternTo simplify correct implementation of the Subject usage patterns described in the preceding section, JBossSX includes an abstract login module that handles the population of the authenticated Subject with a template pattern. The org.jboss.security.auth.spi.AbstractLoginModule class provides concrete implementations of the javax.security.auth.spi.LoginModule interface and offers abstract methods for the key tasks specific to an operation environment security infrastructure. The key details of the class are: package org.jboss.security.auth.spi; /** This class implements the common functionality required for a JAAS server-side LoginModule and implements the JBossSX standard Subject usage pattern of storing identities and roles. Subclass this module to create your own custom LoginModule and override the login(), getRoleSets(), and getIdentity() methods. */ public abstract class AbstractServerLoginModule implements javax.security.auth.spi.LoginModule { protected Subject subject; protected CallbackHandler callbackHandler; protected Map sharedState; protected Map options; ... /** Initialize the login module. This stores the subject, callbackHandler and sharedState, and options for the login session. Subclasses should override if they need to process their own options. A call to super.initialize(...) must be made in the case of an override. @param subject, the Subject to update after a successful login. @param callbackHandler, the CallbackHandler that will be used to obtain the the user identity and credentials. @param sharedState, a Map shared between all configured login module instances @param options, @option password-stacking: if true, the login identity will be taken from the javax.security.auth.login.name value of the sharedState map, and the proof of identity from the javax.security.auth.login.password value of the sharedState map. */ public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { ... } /** Looks for javax.security.auth.login.name and javax.security.auth.login.password values in the sharedState map if the useFirstPass option was true and returns true if they exist. If they do not or are null, this method returns false. Subclasses should override to perform the required credential validation steps. */ public boolean login() throws LoginException { ... } /** Overridden by subclasses to return the Principal that corresponds to the user primary identity. */ abstract protected Principal getIdentity(); /** Overridden by subclasses to return the Groups that correspond to the role sets assigned to the user. Subclasses should create at least a Group named "Roles" that contains the roles assigned to the user. A second common group is "CallerPrincipal," which provides the application identity of the user rather than the security domain identity. @return Group[] containing the sets of roles */ abstract protected Group[] getRoleSets() throws LoginException; } As an alternate base login module, the UsernamePasswordLoginModule further simplifies custom login module implementation by using the string-based username as the user identity and the char[] password as the authentication credential. It also maps anonymous users (indicated by a null username and password) to a Principal. You would typically support anonymous users by setting the Principal that EJBContext.getCallerPrincipal() returns when an unsecure servlet or JavaServer Page (JSP) calls an EJB. The key methods of UsernamePasswordLoginModule are: package org.jboss.security.auth.spi; /** An abstract subclass of AbstractServerLoginModule that imposes a an identity == String username, credentials == String password view on the login process. Subclasses override the getUsersPassword() and getUsersRoles() methods to return the expected password and roles for the user. */ public abstract class UsernamePasswordLoginModule extends AbstractServerLoginModule { /** The login identity */ private Principal identity; /** The proof of login identity */ private char[] credential; /** The principal to use when a null username and password are seen */ private Principal unauthenticatedIdentity; ... /** Override the superclass method to look for an unauthenticatedIdentity property. This method first invokes the super version. @param options, @option unauthenticatedIdentity: the name of the principal to assign and authenticate when a null username and password are seen. */ public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) { super.initialize(subject, callbackHandler, sharedState, options); // Check for unauthenticatedIdentity option. String name = (String) options.get("unauthenticatedIdentity"); if( name != null ) unauthenticatedIdentity = new SimplePrincipal(name); } ... /** A hook that allows subclasses to change the validation of the input password against the expected password. This version checks that neither inputPassword or expectedPassword are null and that inputPassword.equals(expectedPassword) is true; @return true if the inputPassword is valid, false otherwise. */ protected boolean validatePassword(String inputPassword, String expectedPassword) { if( inputPassword == null || expectedPassword == null ) return false; return inputPassword.equals(expectedPassword); } /** Get the expected password for the current username available via the getUsername() method. This is called from within the login() method after the CallbackHandler has returned the username and candidate password. @return the valid password String */ abstract protected String getUsersPassword() throws LoginException; } Write a custom login moduleWhen writing a custom login module that integrates with your security infrastructure, you should start by subclassing org.jboss.security.auth.spi.AbstractLoginModule or one of its subclasses to ensure that your login module provides the authenticated Principal information in the form expected by the JBossSX security manager. When subclassing the AbstractLoginModule, you need to override: void initialize(Subject, CallbackHandler, Map, Map) if you have custom options to parse.boolean login() to perform the authentication activity.Principal getIdentity() to return the Principal object for the user authenticated by the log() step.Group[] getRoleSets() to return at least a Group named Roles that contains the roles assigned to the Principal authenticated during login(). A second common group is CallerPrincipal; it provides, if necessary, the user’s application identity rather than the security domain identity.When subclassing the UsernamePasswordLoginModule, a subclass of AbstractLoginModule, write: void initialize(Subject, CallbackHandler, Map, Map) if you have custom options to parse.String getUsersPassword() to return the expected password for the current username available via the getUsername() method. The getUsersPassword() method is called from within login() after the CallbackHandler returns the username and candidate password.Group[] getRoleSets() to return at least a Group named Roles that contains the roles assigned to the Principal authenticated during login(). A second group CallerPrincipal provides, if necessary, the user’s application identity instead of the security domain identity.An exampleWant to see the security features in action? Download the source code from Resources. I’ll walk you through it using the download archive to set up and deploy the example to a JBoss/Tomcat 2.4 bundle. You’ll also look at the application from the secure and unsecured servlet accessing a secured EJB to demonstrate many of the security features presented in this article. To deploy the example application: You need the Ant build tool. If you don’t already have it, see the Ant homepage.Next, download the JBoss/Tomcat 2.4.x bundle from Sourceforge and unarchive it. At the time of this writing, the current JBoss/Tomcat bundle is JBoss-2.4.0.26_Tomcat-3.2.3.zip; it unarchives to create a JBoss-2.4.0_Tomcat-3.2.3 directory.Next, download the Examples archive and unarchive them.Edit the dist.root property that specifies the JBoss/Tomcat bundle location in the build.xml file, or override the property on the command line to Ant by creating an .ant.properties file in the examples root directory. The default location is /tmp/JBoss-2.4.0_Tomcat-3.2.3, so if you unarchive the JBoss/Tomcat bundle in your /tmp directory, you’re ready to go.Build and deploy the example ears by running Ant within the examples directory. Your output should look something like this: examples 210>ant Buildfile: build.xml validate: fail_if_not_valid: init: [echo] Using jboss.dist=/tmp/JBoss-2.4.0_Tomcat-3.2.3/jboss [echo] Using classpath=/tmp/JBoss-2.4.0_Tomcat-3.2.3/... compile: [mkdir] Created dir: /home/starksm/examples/build/classes [javac] Compiling 7 source files to /home/starksm/examples/build/classes jar1: [mkdir] Created dir: /home/starksm/examples/build/META-INF [copy] Copying 1 file to /home/starksm/examples/build/META-INF [copy] Copying 1 file to /home/starksm/examples/build/META-INF [jar] Building jar: /home/starksm/examples/build/ssbean1.jar war1: [mkdir] Created dir: /home/starksm/examples/build/web/WEB-INF/classes/org/jboss/docs/jaas/howto [copy] Copying 1 file to /home/starksm/examples/build/web/WEB-INF [copy] Copying 1 file to /home/starksm/examples/build/web/WEB-INF [copy] Copying 1 file to /home/starksm/examples/build/web/WEB-INF/classes/org/jboss/docs/jaas/howto [jar] Building jar: /home/starksm/examples/build/tutorial1.war ear1: [copy] Copying 1 file to /home/starksm/examples/build/META-INF [jar] Building jar: /home/starksm/examples/build/tutorial1.ear ... ears: [copy] Copying 1 file to /tmp/JBoss-2.4.0_Tomcat-3.2.3/jboss/deploy [copy] Copying 1 file to /tmp/JBoss-2.4.0_Tomcat-3.2.3/jboss/deploy [mkdir] Created dir: /tmp/JBoss-2.4.0_Tomcat-3.2.3/jboss/conf/jaas_howto [copy] Copying 19 files to /tmp/JBoss-2.4.0_Tomcat-3.2.3/jboss/conf/jaas_howto [copy] Copying 1 file to /tmp/JBoss-2.4.0_Tomcat-3.2.3/jboss/conf/jaas_howto If you see something similar to this: examples 212>ant Buildfile: build.xml validate: fail_if_not_valid: BUILD FAILED /home/starksm/examples/build.xml:34: jboss.dist=/usr/local/JBoss-2.4.0_Tomcat-3.2.3/jboss is not a valid JBoss dist directory you need to correct the dist.root property’s value in the Ant build.xml file in the examples directory as indicated in the previous step.Now start the JBoss server with the jaas_howto configuration, which contains the JAAS login configuration file and was created by the Ant build process. To start JBoss, go to the jboss/bin directory and execute the run.sh or run.bat script as appropriate for your operating system, passing in the jaas_howto config name. Here’s an example from a Linux system with key output emphasized: bin 358>./run.sh jaas_howto JBOSS_CLASSPATH=:run.jar:../lib/crimson.jar jboss.home = /tmp/JBoss-2.4.0_Tomcat-3.2.3/jboss Using JAAS LoginConfig: file:/tmp/JBoss-2.4.0_Tomcat-3.2.3/jboss/conf/jaas_howto/auth.conf Using configuration "jaas_howto" [root] Started Log4jService, config=file:/tmp/JBoss-2.4.0_Tomcat-3.2.3/jboss/conf/jaas_howto/log4j.properties [Info] Java version: 1.3.1,Sun Microsystems Inc. [Info] Java VM: Java HotSpot(TM) Server VM 1.3.1-b24,Sun Microsystems Inc. [Info] System: Linux 2.4.3-12,i386 ... [Container factory] Deployed application: file:/tmp/JBoss-2.4.0_Tomcat-3.2.3/jboss/tmp/deploy/Default/tutorial1.ear [J2EE Deployer Default] Starting module tutorial1.war [EmbeddedTomcatSX] deploy, ctxPath=/jaas-example1, warUrl=file:/tmp/JBoss-2.4.0_Tomcat-3.2.3/jboss/tmp/deploy/Default/tutorial1.ear/web1003/ [J2EE Deployer Default] J2EE application: file:/tmp/JBoss-2.4.0_Tomcat-3.2.3/jboss/deploy/tutorial1.ear is deployed. [Service Control] Started 28 services [Default] JBoss 2.4 BETA(Rel_2_4_0_25) Started in 0m:15s The tutorial1.ear contentstutorial1.ear consists of a single Webpage, a secured servlet, an unsecured servlet, and two secured stateless session beans. As the servlet and session bean code is trivial, I’ll leave its inspection as an exercise for you. The interesting aspect of the example is the deployment descriptors’ security-related elements and the configuration of the JAAS login module associated with the security domain. tutorial1.ear‘s contents are shown below with the security-related files emphasized: tutorial1.ear + META-INF |-- MANIFEST.MF |-- application.xml + ssbean1.jar |-- + META-INF |----- ejb-jar.xml |----- jboss.xml |-- + org/jboss/docs/jaas/howto |----- Session.class |----- SessionHome.class |----- PrivateSessionBean.class |----- PublicSessionBean.class |-- roles.properties |-- users.properties + tutorial1.war |-- + WEB-INF |----- web.xml |----- jboss-web.xml |----+ classes/org/jboss/docs/jaas/howto |------ EJBServlet.class |-- index.html We’ll look at each file in turn. The tutorial1.ear/ssbean1.jar/META-INF/ejb-jar.xml descriptor The following ejb-jar.xml deployment descriptor declares the tutorial EJBs and their security requirements: <?xml version = "1.0" encoding = "UTF-8"?> <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN" "http://java.sun.com/dtd/ejb-jar_2_0.dtd"> <ejb-jar> <display-name>SecurityTests</display-name> <enterprise-beans> <session> <description>A trivial stateless session echo bean</description> <ejb-name>PublicSession</ejb-name> <home>org.jboss.docs.jaas.howto.SessionHome</home> <remote>org.jboss.docs.jaas.howto.Session</remote> <ejb-class>org.jboss.docs.jaas.howto.PublicSessionBean</ejb-class> <session-type>Stateless</session-type> <transaction-type>Container</transaction-type> <ejb-ref> <ejb-ref-name>ejb/PrivateSession</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home>org.jboss.docs.jaas.howto.SessionHome</home> <remote>org.jboss.docs.jaas.howto.Session</remote> <ejb-link>PrivateSession</ejb-link> </ejb-ref> <security-role-ref> <role-name>EchoUser</role-name> <role-link>Echo</role-link> </security-role-ref> <security-identity> <run-as> <role-name>InternalUser</role-name> </run-as> </security-identity> </session> <session> <description>A trivial stateful session echo bean</description> <ejb-name>PrivateSession</ejb-name> <home>org.jboss.docs.jaas.howto.SessionHome</home> <remote>org.jboss.docs.jaas.howto.Session</remote> <ejb-class>org.jboss.docs.jaas.howto.PrivateSessionBean</ejb-class> <session-type>Stateful</session-type> <transaction-type>Container</transaction-type> <security-role-ref> <role-name>InternalUser</role-name> <role-link>InternalUser</role-link> </security-role-ref> </session> </enterprise-beans> <assembly-descriptor> <security-role> <role-name>Coder</role-name> </security-role> <security-role> <role-name>Echo</role-name> </security-role> <security-role> <role-name>InternalUser</role-name> </security-role> <method-permission> <role-name>Echo</role-name> <method> <ejb-name>PublicSession</ejb-name> <method-name>*</method-name> </method> </method-permission> <method-permission> <role-name>InternalUser</role-name> <method> <ejb-name>PrivateSession</ejb-name> <method-name>*</method-name> </method> </method-permission> <method-permission> <role-name>Coder</role-name> <method> <ejb-name>PublicSession</ejb-name> <method-name>create</method-name> </method> <method> <ejb-name>PublicSession</ejb-name> <method-name>remove</method-name> </method> </method-permission> <method-permission> <unchecked/> <method> <ejb-name>PublicSession</ejb-name> <method-name>noop</method-name> </method> </method-permission> <exclude-list> <description>Methods that cannot be used in this deployment</description> <method> <ejb-name>PublicSession</ejb-name> <method-name>restricted</method-name> </method> </exclude-list> </assembly-descriptor> </ejb-jar> Items of note include: The security-role-ref element is where the PublicSession bean declares an EchoUser role name, which links to the Echo role name. That element indicates that the PublicSession bean queries the EJBContext.isCallerInRole with a EchoUser role name string.The security-identity/run-as/role-name InternalUser declaration indicates that a principal with a role named InternalUser handles any EJB calls made by the PublicSession bean.The PrivateSession bean declares an InternalUser role name to indicate that the bean queries the EJBContext.isCallerInRole with an InternalUser role name string.In the assembly-descriptor section, logical roles of Echo, Coder, and InternalUser are declared using security-role elements.The Echo role receives permission to access any method in the PublicSession bean in the first method-permission element.The InternalUser role receives permission to access any PrivateSession bean method in the second method-permission element.The fourth method-permission element indicates that any authenticated user can call the PublicSession noop method with the unchecked element instead of a role-name element.The exclude-list element declares that no one can call the PublicSession restricted method.The tutorial1.ear/ssbean1.jar/META-INF/jboss.xml descriptor The code below is the JBoss-server-specific EJB deployment descriptor: <?xml version="1.0" encoding="UTF-8"?> <jboss> <!-- All bean containers use this security manager by default --> <security-domain>java:/jaas/example1</security-domain> <enterprise-beans> <session> <ejb-name>PublicSession</ejb-name> <jndi-name>example1/PublicSession</jndi-name> </session> <session> <ejb-name>PrivateSession</ejb-name> <jndi-name>example1/PrivateSession</jndi-name> </session> </enterprise-beans> </jboss> This descriptor indicates that the security manager located at the JNDI name java:/jaas/example1 secures all EJBs in the ssbean1.jar. The JNDI name’s final component (example1) determines which login modules are associated with the security domain. The tutorial1.ear/ssbean1.jar/roles.properties, users.properties files The org.jboss.security.auth.spi.UsersRolesLoginModule custom JAAS login module shipped with the JBossSX framework uses the roles.properties and users.properties files. This simple login module uses Java properties format files for mapping usernames to passwords and usernames to roles names. The roles.properties file maps a username to one or more role names using the format username[.RoleGroup]=role1_name[,role2_name,...]. A RoleGroup names the Group that will be created to hold given roles. With no specified RoleGroup, it defaults to Roles. Here’s the roles.properties file for the example: # roles.properties java=Echo duke=Java,Coder java.CallerPrincipal=caller_java duke.CallerPrincipal=caller_duke The code above maps the username java to the role name Echo under the Roles RoleGroup, and the role name caller_java under the CallerPrincipal RoleGroup. The users.properties file maps a username to a user password. Here’s the users.properties file for the example: # users.properties java=echoman duke=javaman The above code maps the username java to the password echoman. The tutorial1.ear/tutorial1.war/WEB-INF/web.xml descriptor The following web.xml deployment descriptor declares the tutorial servlets and their EJB references and security requirements: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <!-- ### Servlets --> <servlet> <servlet-name>SecureServlet</servlet-name> <servlet-class>org.jboss.docs.jaas.howto.EJBServlet</servlet-class> </servlet> <servlet> <servlet-name>UnsecureServlet</servlet-name> <servlet-class>org.jboss.docs.jaas.howto.EJBServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>SecureServlet</servlet-name> <url-pattern>/restricted/SecureServlet</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>UnsecureServlet</servlet-name> <url-pattern>/UnsecureServlet</url-pattern> </servlet-mapping> <!-- ### Security --> <security-constraint> <web-resource-collection> <web-resource-name>Restricted</web-resource-name> <description>Declarative security tests</description> <url-pattern>/restricted/*</url-pattern> <http-method>HEAD</http-method> <http-method>GET</http-method> <http-method>POST</http-method> <http-method>PUT</http-method> <http-method>DELETE</http-method> </web-resource-collection> <auth-constraint> <role-name>Echo</role-name> </auth-constraint> <user-data-constraint> <description>no description</description> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> <realm-name>JAAS Tutorial Servlets</realm-name> </login-config> <security-role> <description>A user allowed to invoke echo methods</description> <role-name>Echo</role-name> </security-role> <!-- ### EJB References (java:comp/env/ejb) --> <ejb-ref> <ejb-ref-name>ejb/SecuredEJB</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home>org.jboss.docs.jaas.howto.SessionHome</home> <remote>org.jboss.docs.jaas.howto.Session</remote> </ejb-ref> <ejb-ref> <ejb-ref-name>ejb/PrivateEJB</ejb-ref-name> <ejb-ref-type>Session</ejb-ref-type> <home>org.jboss.docs.jaas.howto.SessionHome</home> <remote>org.jboss.docs.jaas.howto.Session</remote> </ejb-ref> </web-app> The security items of note include: The security-constraint/web-resource-collection/url-pattern element declaration indicates that all content under /restricted must be secured for the indicated HTTP methods.The auth-constraint/role-name element indicates that only users with an Echo role can access the content under /restricted.The login-config/auth-method element with the BASIC value indicates that BASIC HTTP authorization will authenticate users attempting to access content under /restricted.The SecureServlet requires authenticated access, since its servlet-mapping/url-pattern element is under the /restricted path.The tutorial1.ear/tutorial1.war/WEB-INF/jboss-web.xml descriptor Here is the JBoss-server-specific Web application deployment descriptor: <?xml version="1.0" encoding="UTF-8"?> <jboss-web> <security-domain>java:/jaas/example1</security-domain> <ejb-ref> <ejb-ref-name>ejb/SecuredEJB</ejb-ref-name> <jndi-name>example1/PublicSession</jndi-name> </ejb-ref> <ejb-ref> <ejb-ref-name>ejb/PrivateEJB</ejb-ref-name> <jndi-name>example1/PrivateSession</jndi-name> </ejb-ref> </jboss-web> This code indicates that the security manager located at the JNDI name java:/jaas/example1 handles security for all secured Web content. Since the security-domain element matches that used by the jboss.xml deployment descriptor, the same security manager secures the ear EJBs and Web content. The jboss/conf/jaas_howto/auth.conf login configuration file The final security-related file is outside of tutorial1.ear. The auth.conf file in the JBoss server configuration directory is the JAAS login configuration file. That file consists of named login configuration entries. Here’s the version of auth.conf that we will use for the tutorial: example1 { // A properties file LoginModule that supports CallerPrincipal mapping org.jboss.security.auth.spi.UsersRolesLoginModule required unauthenticatedIdentity=nobody ; }; example2 { /* A JDBC-based LoginModule LoginModule options: dsJndiName: The name of the DataSource of the database containing the Principals, Roles tables principalsQuery: The prepared statement query equivalent to: "select Password from Principals where PrincipalID=?" rolesQuery: The prepared statement query equivalent to: "select Role, RoleGroup from Roles where PrincipalID=?" */ org.jboss.security.auth.spi.DatabaseServerLoginModule required dsJndiName="java:/DefaultDS" principalsQuery="select Password from Principals where PrincipalID=?" rolesQuery="select Role, RoleGroup from Roles where PrincipalID=?" unauthenticatedIdentity=nobody ; }; In the example file above, there are two entries, example1 and example2. The example1 entry contains a single login module whose class name is org.jboss.security.auth.spi.UsersRolesLoginModule, which is required for successful authentication. The login module is passed a single option named unauthenticatedIdentity with a value of nobody. The unauthenticatedIdentity option authenticates anonymous users as the identity nobody. The unauthenticatedIdentity typically provides an identity to unsecured Web content that calls EJBs that use the EJBContext.getCallerPrincipal() method. Since this method cannot return a null value as per the EJB specification, the application server must provide a mechanism to map the nonexistent user onto an identity. JBoss pushes this responsibility onto the security domain login modules. Test the tutorial1.ear deployment The example’s final step is to test that the security constraints do in fact work. If your browser runs on the same host as the JBoss server, launch your Web browser and open the Web application index.html file located at http://localhost:8080/jaas-example1/index.html. Figure 7 shows what the Webpage should look like. Each link is a test case, with the expected result shown as [PASS] or [FAIL] based on the specified security. We’ll walk through links 1 and 4 to verify both a passing and failing test. The first link corresponds to the call sequence illustrated in Figure 8. Each access or method invocation lists the role that the caller must possess for the access to be granted. Traverse Link 1 to invoke the SecureServlet and enter java for the username and echoman as the password in the login dialog. Figure 9 shows the browser result. The JBoss server console should display the following output: [Default] User 'nobody' authenticated. [Default] User 'java' authenticated. [Default] PublicSessionBean.ejbCreate() called [Default] PublicSessionBean.echo, arg=Hello [Default] PublicSessionBean.echo, callerPrincipal=caller_java [Default] PublicSessionBean.echo, isCallerInRole('EchoUser')=true [Default] PrivateSessionBean.ejbCreate() called [Default] PublicSessionBean.echo, created PrivateSession [Default] PrivateSessionBean.echo, arg=Hello [Default] PrivateSessionBean.echo, callerPrincipal=caller_java [Default] PrivateSessionBean.echo, isCallerInRole('InternalUser')=false The first User 'nobody' authenticated. line results because Tomcat tried to determine whether the servlet request had a remote user associated with it. That caused a query to the security interceptor with a null username and password, because the browser was not asked to provide any login information. The null username and password is authenticated as the anonymous user nobody we configured in the server auth.conf file. The following User 'java' authenticated. line is the result of the HTTP BASIC authorization login. Note that PrivateSessionBean.echo() method’s check to see if the caller has the InternalUser role returns false. This seemingly incorrect result is in accord with our interpretation of the EJB 2.0 PFD2 spec, which states on page 439: Note that isCallerInRole(String roleName) tests the principal that represents the caller of the enterprise bean, not the principal that corresponds to the run-as security identity for the bean, if any.Now follow Link 4 to try to access the PrivateSession bean’s echo method from the SecureServlet. That should fail, since only the PublicSession has been configured to run as the InternalUser role. You should see a 500 error and a root cause exception with the message: Insufficient method permissions, principal=java, method=create, requiredRoles=[InternalUser]; This message verifies that the method-permission element, which required a InternalUser role, is enforced. I encourage you to try the additional test cases in the tutorial1.ear example and investigate the second example, tutorial2.ear, which was created and deployed during the examples build. The tutorial2.ear example differs from tutorial1.ear only in its choice of security domain name and thus login module configuration. tutorial2.ear uses a Java Database Connectivity-based login module to demonstrate how to access security information from a database. Secure your J2EE appsAlthough the mapping of application security roles onto a application server deployment environment is currently a nonportable, application-server-specific task, this article has demonstrated how the standard JAAS security could be used to implement the J2EE declarative security model using a simple Subject-based usage pattern. Hopefully, future versions of J2EE will extend the portability of security closer to the deployment layer using similar techniques based on JAAS or equivalent standards-based APIs. This article has peered into the JBossSX security manager implementation of the J2EE declarative security model used by the JBoss EJB and Web containers. It focused on how the JAAS login modules configured for a security domain provide the security information via a Subject usage pattern. I also covered the steps required to create a custom login module for security infrastructures not supported by bundled JBossSX login modules. With this information, you can secure your enterprise applications either by configuring the login modules bundled with JBoss or by writing your own. :END_BODY Scott Stark currently serves as the lead developer for the JBossSX security extension framework and one of the lead developers for the JBoss kernel and the JBoss release manager; he is also a partner in the JBoss Group. He has a Ph.D. in chemical engineering; that field was where he started working with parallel/distributed computing on machines like the BBN TC2000 and CM2. Scott has been doing distributed computing for more than 10 years, using a number of languages and paradigms. His Java experience includes more than four years of work in the technologies that make up the J2EE platform. :END_BIO Web DevelopmentJavaSecurity