Getting Started Securing Enterprise Applications

We are working on a fresh, updated Jakarta EE Tutorial. This section hasn’t yet been updated.

This chapter describes how to administer security for enterprise applications.

Basic Security Tasks for Enterprise Applications

System administrators, application developers, bean providers, and deployers are responsible for administering security for enterprise applications. The basic security tasks are as follows:

  • Setting up a database of users and assigning them to the proper group

  • Setting up identity propagation

  • Setting GlassFish Server properties that enable the applications to run properly. Note that the the Default Principal To Role Mapping setting is enabled by default in the GlassFish Server Administration Console.

  • Annotating the classes and methods of an enterprise application to provide information about which methods need to have restricted access

The sections on the security examples in this chapter and the previous chapter explain how to perform these tasks.

Securing Enterprise Beans

Enterprise beans are Jakarta EE components that implement enterprise bean technology. Enterprise beans run in the enterprise bean container, a runtime environment within GlassFish Server. Although transparent to the application developer, the enterprise bean container provides system-level services, such as transactions and security to its enterprise beans, which form the core of transactional Jakarta EE applications.

Enterprise bean methods can be secured in either of the following ways.

  • Declarative security (preferred): Expresses an application component’s security requirements using either deployment descriptors or annotations. The presence of an annotation in the business method of an enterprise bean class that specifies method permissions is all that is needed for method protection and authentication in some situations. This section discusses this simple and efficient method of securing enterprise beans.

    Because of some limitations to the simplified method of securing enterprise beans, you would want to continue to use the deployment descriptor to specify security information in some instances. An authentication mechanism must be configured on the server for the simple solution to work. Basic authentication is GlassFish Server’s default authentication method.

    This tutorial explains how to invoke user name/password authentication of authorized users by decorating the enterprise application’s business methods with annotations that specify method permissions.

    To make the deployer’s task easier, the application developer can define security roles. A security role is a grouping of permissions that a given type of application users must have in order to successfully use the application. For example, in a payroll application, some users will want to view their own payroll information (employee), some will need to view others' payroll information (manager), and some will need to be able to change others' payroll information (payrollDept). The application developer would determine the potential users of the application and which methods would be accessible to which users. The application developer would then decorate classes or methods of the enterprise bean with annotations that specify the types of users authorized to access those methods. Using annotations to specify authorized users is described in Specifying Authorized Users by Declaring Security Roles.

    When one of the annotations is used to define method permissions, the deployment system will automatically require user name/password authentication. In this type of authentication, a user is prompted to enter a user name and password, which will be compared against a database of known users. If the user is found and the password matches, the roles that the user is assigned will be compared against the roles that are authorized to access the method. If the user is authenticated and found to have a role that is authorized to access that method, the data will be returned to the user.

    Using declarative security is discussed in Securing an Enterprise Bean Using Declarative Security.

  • Programmatic security: For an enterprise bean, code embedded in a business method that is used to access a caller’s identity programmatically and that uses this information to make security decisions. Programmatic security is useful when declarative security alone is not sufficient to express the security model of an application.

    In general, security management should be enforced by the container in a manner that is transparent to the enterprise beans' business methods. The programmatic security APIs described in this chapter should be used only in the less frequent situations in which the enterprise bean business methods need to access the security-context information, such as when you want to grant access based on the time of day or other nontrivial condition checks for a particular role.

    Programmatic security is discussed in Securing an Enterprise Bean Programmatically.

Some of the material in this chapter assumes that you have already read Enterprise Beans, Getting Started with Enterprise Beans, and Introduction to Security in the Jakarta EE Platform.

This section discusses securing a Jakarta EE application where one or more modules, such as enterprise bean JAR files, are packaged into an EAR file, the archive file that holds the application. Security annotations will be used in the Java programming class files to specify authorized users and basic, or user name/password, authentication.

Enterprise beans often provide the business logic of a web application. In these cases, packaging the enterprise bean within the web application’s WAR module simplifies deployment and application organization. Enterprise beans may be packaged within a WAR module as Java class files or within a JAR file that is bundled within the WAR module. When a servlet or Jakarta Faces page handles the web front end and the application is packaged into a WAR module as a Java class file, security for the application can be handled in the application’s web.xml file. The enterprise bean in the WAR file can have its own deployment descriptor, ejb-jar.xml, if required. Securing web applications using web.xml is discussed in Getting Started Securing Web Applications.

The following sections describe declarative and programmatic security mechanisms that can be used to protect enterprise bean resources. The protected resources include enterprise bean methods that are called from application clients, web components, or other enterprise beans.

For more information on this topic, read the Jakarta Enterprise Beans 4.0 specification. This document can be downloaded from https://jakarta.ee/specifications/enterprise-beans/4.0/. Chapter 11 of this specification, "Security Management", discusses security management for enterprise beans.

Securing an Enterprise Bean Using Declarative Security

Declarative security enables the application developer to specify which users are authorized to access which methods of the enterprise beans and to authenticate these users with basic, or user name/password, authentication. Frequently, the person who is developing an enterprise application is not the same person who is responsible for deploying the application. An application developer who uses declarative security to define method permissions and authentication mechanisms is passing along to the deployer a security view of the enterprise beans contained in the enterprise bean JAR. When a security view is passed on to the deployer, he or she uses this information to define method permissions for security roles. If you don’t define a security view, the deployer will have to determine what each business method does to determine which users are authorized to call each method.

A security view consists of a set of security roles, a semantic grouping of permissions that a given type of users of an application must have to successfully access the application. Security roles are meant to be logical roles, representing a type of user. You can define method permissions for each security role. A method permission is a permission to invoke a specified group of methods of an enterprise bean’s business interface, home interface, component interface, and/or web service endpoints. After method permissions are defined, user name/password authentication will be used to verify the identity of the user.

It is important to keep in mind that security roles are used to define the logical security view of an application. They should not be confused with the user groups, users, principals, and other concepts that exist in GlassFish Server. Note that the Jakarta Security requires that group principal names be mapped to roles of the same name by default, but that implementations of the standard may allow configuration of a different default. In GlassFish Server, you do not need to perform any additional steps to map the roles defined in the application to users, groups, and principals that are the components of the user database in the file realm. This mapping is set by default in the GlassFish Server Administration Console as described in Mapping Roles to Users and Groups>.

The following sections show how an application developer uses declarative security to either secure an application or to create a security view to pass along to the deployer.

Specifying Authorized Users by Declaring Security Roles

This section discusses how to use annotations to specify the method permissions for the methods of a bean class. For more information on these annotations, refer to Jakarta Annotations specification at https://jakarta.ee/specifications/annotations/2.0/.

Method permissions can be specified on the class, the business methods of the class, or both. Method permissions can be specified on a method of the bean class to override the method permissions value specified on the entire bean class. The following annotations are used to specify method permissions.

  • @DeclareRoles: Specifies all the roles that the application will use, including roles not specifically named in a @RolesAllowed annotation. The set of security roles the application uses is the total of the security roles defined in the @DeclareRoles and @RolesAllowed annotations.

    The @DeclareRoles annotation is specified on a bean class, where it serves to declare roles that can be tested (for example, by calling isCallerInRole) from within the methods of the annotated class. When declaring the name of a role used as a parameter to the isCallerInRole(String roleName) method, the declared name must be the same as the parameter value.

    The following example code demonstrates the use of the @DeclareRoles annotation:

    @DeclareRoles("BusinessAdmin")
    public class Calculator {
        ...
    }

    The syntax for declaring more than one role is as shown in the following example:

    @DeclareRoles({"Administrator", "Manager", "Employee"})
  • @RolesAllowed("list-of-roles"): Specifies the security roles permitted to access methods in an application. This annotation can be specified on a class or on one or more methods. When specified at the class level, the annotation applies to all methods in the class. When specified on a method, the annotation applies to that method only and overrides any values specified at the class level.

    To specify that no roles are authorized to access methods in an application, use the @DenyAll annotation. To specify that a user in any role is authorized to access the application, use the @PermitAll annotation.

    When used in conjunction with the @DeclareRoles annotation, the combined set of security roles is used by the application.

    The following example code demonstrates the use of the @RolesAllowed annotation:

    @DeclareRoles({"Administrator", "Manager", "Employee"})
    public class Calculator {
    
        @RolesAllowed("Administrator")
        public void setNewRate(int rate) {
            ...
        }
    }
  • @PermitAll: Specifies that all security roles are permitted to execute the specified method or methods. The user is not checked against a database to ensure that he or she is authorized to access this application.

    This annotation can be specified on a class or on one or more methods. Specifying this annotation on the class means that it applies to all methods of the class. Specifying it at the method level means that it applies to only that method.

    The following example code demonstrates the use of the @PermitAll annotation:

    import jakarta.annotation.security.*;
    @RolesAllowed("RestrictedUsers")
    public class Calculator {
    
        @RolesAllowed("Administrator")
        public void setNewRate(int rate) {
            //...
        }
        @PermitAll
        public long convertCurrency(long amount) {
            //...
        }
    }
  • @DenyAll: Specifies that no security roles are permitted to execute the specified method or methods. This means that these methods are excluded from execution in the Jakarta EE container.

    The following example code demonstrates the use of the @DenyAll annotation:

    import jakarta.annotation.security.*;
    @RolesAllowed("Users")
    public class Calculator {
        @RolesAllowed("Administrator")
        public void setNewRate(int rate) {
            //...
        }
        @DenyAll
        public long convertCurrency(long amount) {
            //...
        }
    }

The following code snippet demonstrates the use of the @DeclareRoles annotation with the isCallerInRole method. In this example, the @DeclareRoles annotation declares a role that the enterprise bean PayrollBean uses to make the security check by using isCallerInRole("payroll") to verify that the caller is authorized to change salary data:

@DeclareRoles("payroll")
@Stateless public class PayrollBean implements Payroll {
    @Resource SessionContext ctx;

    public void updateEmployeeInfo(EmplInfo info) {

        oldInfo = ... read from database;

        // The salary field can be changed only by callers
        // who have the security role "payroll"
        Principal callerPrincipal = ctx.getCallerPrincipal();
        if (info.salary != oldInfo.salary && !ctx.isCallerInRole("payroll")) {
            throw new SecurityException(...);
        }
        ...
    }
    ...
}

The following example code illustrates the use of the @RolesAllowed annotation:

@RolesAllowed("admin")
public class SomeClass {
    public void aMethod () {...}
    public void bMethod () {...}
    ...
}

@Stateless
public class MyBean extends SomeClass implements A  {

    @RolesAllowed("HR")
    public void aMethod () {...}

    public void cMethod () {...}
    ...
}

In this example, assuming that aMethod, bMethod, and cMethod are methods of business interface A, the method permissions values of methods aMethod and bMethod are @RolesAllowed("HR") and @RolesAllowed("admin"), respectively. The method permissions for method cMethod have not been specified.

To clarify, the annotations are not inherited by the subclass itself. Instead, the annotations apply to methods of the superclass that are inherited by the subclass.

Specifying an Authentication Mechanism and Secure Connection

When method permissions are specified, basic user name/password authentication will be invoked by GlassFish Server.

To use a different type of authentication or to require a secure connection using SSL, specify this information in an application deployment descriptor.

Securing an Enterprise Bean Programmatically

Programmatic security, code that is embedded in a business method, is used to access a caller’s identity programmatically and uses this information to make security decisions within the method itself.

In general, security management should be enforced by the container in a manner that is transparent to the enterprise bean’s business methods. This section describes the SecurityContext API and security-related methods of the EJBContext API. The newer SecurityContext API duplicates some functions of the EJBContext API because it is intended to provide a consistent API across containers. These security APIs should be used only in the less frequent situations in which the enterprise bean business methods need to access the security context information.

The SecurityContext interface, as specified in the Jakarta Security specification, defines three methods that allow the bean provider to access security information about the enterprise bean’s caller:

  • getCallerPrincipal() retrieves the Principal that represents the name of the authenticated caller. This is the container-specific representation of the caller principal, and the type may differ from the type of the caller principal originally established by an HttpAuthenticationMechanism. This method returns null for an unauthenticated caller. Note that this behavior differs from the behavior of the EJBContext.getCallerPrincipal() method, which returns a (vendor-specific) special principal to represent an anonymous caller.

  • getPrincipalsByType() retrieves all principals of the given type from the authenticated caller’s Subject. This method returns an empty Set if the caller is unauthenticated, or if the requested type is not found.

    Where both a container caller principal and an application caller principal are present, the value returned by getName() is the same for both principals.

  • isCallerInRole() takes a String argument that represents the role to be tested. The specification does not define how the role determination is made, but the result must be the same as if the corresponding container-specific call had been made (for example EJBContext.isCallerInRole()), and must be consistent with the result implied by specifications that prescribe role-mapping behavior.

The jakarta.ejb.EJBContext interface provides two methods that allow the bean provider to access security information about the enterprise bean’s caller.

  • getCallerPrincipal allows the enterprise bean methods to obtain the current caller principal’s name. The methods might, for example, use the name as a key to information in a database. This method never returns null. Instead, it returns a (vendor-specific) principal with a special username to indicate an anonymous/unauthenticated caller. Note that this behavior differs from the behavior of the SecurityContext.getCallerPrincipal() method, which returns null for an unauthenticated caller.

    The following code sample illustrates the use of the getCallerPrincipal method:

    @Stateless
    public class EmployeeServiceBean implements EmployeeService {
        @Resource
        SessionContext ctx;
    
        @PersistenceContext
        EntityManager em;
    
        public void changePhoneNumber(...) {
            ...
            // obtain the caller principal
            callerPrincipal = ctx.getCallerPrincipal();
    
            // obtain the caller principal's name
            callerKey = callerPrincipal.getName();
    
            // use callerKey as primary key to find EmployeeRecord
            EmployeeRecord myEmployeeRecord =
                em.find(EmployeeRecord.class, callerKey);
    
            // update phone number
            myEmployeeRecord.setPhoneNumber(...);
    
            ...
        }
    }

    In this example, the enterprise bean obtains the principal name of the current caller and uses it as the primary key to locate an EmployeeRecord entity. This example assumes that application has been deployed such that the current caller principal contains the primary key used for the identification of employees (for example, employee number).

  • isCallerInRole allows the developer to code the security checks that cannot be easily defined using method permissions. Such a check might impose a role-based limit on a request, or it might depend on information stored in the database.

    The enterprise bean code can use the isCallerInRole method to test whether the current caller has been assigned to a given security role. Security roles are defined by the bean provider or the application assembler and are assigned by the deployer to principals or principal groups that exist in the operational environment.

    The following code sample illustrates the use of the isCallerInRole method:

    @Stateless
    public class PayrollBean implements Payroll {
        @Resource
        SessionContext ctx;
    
        public void updateEmployeeInfo(EmplInfo info) {
    
            oldInfo = ... read from database;
    
            // The salary field can be changed only by callers
            // who have the security role "payroll"
            if (info.salary != oldInfo.salary &&
                !ctx.isCallerInRole("payroll")) {
                    throw new SecurityException(...);
            }
            ...
        }
        ...
    }

You would use programmatic security in this way to dynamically control access to a method, for example, when you want to deny access except during a particular time of day. An example application that uses the getCallerPrincipal and isCallerInRole methods is described in The converter-secure Example: Securing an Enterprise Bean with Programmatic Security.

Propagating a Security Identity (Run-As)

You can specify whether a caller’s security identity should be used for the execution of specified methods of an enterprise bean or whether a specific run-as identity should be used. Figure 1, “Security Identity Propagation” illustrates this concept.

Diagram of security identity propagation from client to intermediate container to target container
Figure 1. Security Identity Propagation

In this illustration, an application client is making a call to an enterprise bean method in one enterprise bean container. This enterprise bean method, in turn, makes a call to an enterprise bean method in another container. The security identity during the first call is the identity of the caller. The security identity during the second call can be any of the following options.

  • By default, the identity of the caller of the intermediate component is propagated to the target enterprise bean. This technique is used when the target container trusts the intermediate container.

  • A specific identity is propagated to the target enterprise bean. This technique is used when the target container expects access using a specific identity.

To propagate an identity to the target enterprise bean, configure a run-as identity for the bean, as described in Configuring a Component’s Propagated Security Identity. Establishing a run-as identity for an enterprise bean does not affect the identities of its callers, which are the identities tested for permission to access the methods of the enterprise bean. The run-as identity establishes the identity that the enterprise bean will use when it makes calls.

The run-as identity applies to the enterprise bean as a whole, including all the methods of the enterprise bean’s business interface, local and remote interfaces, component interface, and web service endpoint interfaces, the message listener methods of a message-driven bean, the timeout method of an enterprise bean, and all internal methods of the bean that might be called in turn.

Configuring a Component’s Propagated Security Identity

You can configure an enterprise bean’s run-as, or propagated, security identity by using the @RunAs annotation, which defines the role of the application during execution in a Jakarta EE container. The annotation can be specified on a class, allowing developers to execute an application under a particular role. The role must map to the user/group information in the container’s security realm. The @RunAs annotation specifies the name of a security role as its parameter.

The following code demonstrates the use of the @RunAs annotation:

@RunAs("Admin")
public class Calculator {
    //....
}

You will have to map the run-as role name to a given principal defined in GlassFish Server if the given roles are associated with more than one user principal.

Trust between Containers

When an enterprise bean is designed so that either the original caller identity or a designated identity is used to call a target bean, the target bean will receive the propagated identity only. The target bean will not receive any authentication data.

There is no way for the target container to authenticate the propagated security identity. However, because the security identity is used in authorization checks (for example, method permissions or with the isCallerInRole method), it is vitally important that the security identity be authentic. Because no authentication data is available to authenticate the propagated identity, the target must trust that the calling container has propagated an authenticated security identity.

By default, GlassFish Server is configured to trust identities that are propagated from different containers. Therefore, you do not need to take any special steps to set up a trust relationship.

Deploying Secure Enterprise Beans

The deployer is responsible for ensuring that an assembled application is secure after it has been deployed in the target operational environment. If a security view has been provided to the deployer through the use of security annotations and/or a deployment descriptor, the security view is mapped to the mechanisms and policies used by the security domain in the target operational environment, which in this case is GlassFish Server. If no security view is provided, the deployer must set up the appropriate security policy for the enterprise bean application.

Deployment information is specific to a web or application server.

Examples: Securing Enterprise Beans

The following examples show how to secure enterprise beans using declarative and programmatic security.

The cart-secure Example: Securing an Enterprise Bean with Declarative Security

This section discusses how to configure an enterprise bean for basic user name/password authentication. When a bean that is constrained in this way is requested, the server requests a user name and password from the client and verifies that the user name and password are valid by comparing them against a database of authorized users in GlassFish Server.

If the topic of authentication is new to you, see Specifying Authentication Mechanisms.

This example demonstrates security by starting with the unsecured enterprise bean application, cart, which is found in the jakartaee-examples/tutorial/ejb/cart/ directory and is discussed in The cart Example.

In general, the following steps are necessary to add user name/password authentication to an existing application that contains an enterprise bean. In the example application included with this tutorial, these steps have been completed for you and are listed here simply to show what needs to be done should you wish to create a similar application.

  1. Create an application like the one in The cart Example. The example in this tutorial starts with this example and demonstrates adding basic authentication of the client to this application. The example application discussed in this section can be found at _jakartaee-examples/tutorial/security/cart-secure/.

  2. If you have not already done so, complete the steps in To Set Up Your System for Running the Security Examples to configure your system for running the tutorial applications.

  3. Modify the source code for the enterprise bean, CartBean.java, to specify which roles are authorized to access which protected methods. This step is discussed in Annotating the Bean.

  4. Build, package, and deploy the enterprise bean; then build and run the client application by following the steps in To Run the cart-secure Example Using NetBeans IDE or To Run the cart-secure Example Using Maven.

Annotating the Bean

The source code for the original cart application was modified as shown in the following code snippet (modifications in bold). The resulting file can be found in the file jakartaee-examples/tutorial/security/cart-secure/cart-secure-ejb/src/main/java/jakarta/tutorial/cart/ejb/CartBean.java.

The code snippet is as follows:

package ee.jakarta.tutorial.cartsecure.ejb;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import ee.jakarta.tutorial.cart.util.BookException;
import ee.jakarta.tutorial.cart.util.IdVerifier;
import jakarta.ejb.Remove;
import jakarta.ejb.Stateful;
import jakarta.annotation.security.DeclareRoles;
import jakarta.annotation.security.RolesAllowed;

@Stateful
@DeclareRoles("TutorialUser")
public class CartBean implements Cart, Serializable {
    List<String> contents;
    String customerId;
    String customerName;

    @Override
    public void initialize(String person) throws BookException {
        if (person == null) {
            throw new BookException("Null person not allowed.");
        } else {
            customerName = person;
        }

        customerId = "0";
        contents = new ArrayList<>();
    }

    @Override
    public void initialize(String person, String id) throws BookException {
        if (person == null) {
            throw new BookException("Null person not allowed.");
        } else {
            customerName = person;
        }

        IdVerifier idChecker = new IdVerifier();

        if (idChecker.validate(id)) {
            customerId = id;
        } else {
            throw new BookException("Invalid id: " + id);
        }

        contents = new ArrayList<>();
    }

    @Override
    @RolesAllowed("TutorialUser")
    public void addBook(String title) {
        contents.add(title);
    }

    @Override
    @RolesAllowed("TutorialUser")
    public void removeBook(String title) throws BookException {
        boolean result = contents.remove(title);

        if (result == false) {
            throw new BookException("\"" + title + "\" not in cart.");
        }
    }

    @Override
    @RolesAllowed("TutorialUser")
    public List<String> getContents() {
        return contents;
    }

    @Override
    @Remove()
    @RolesAllowed("TutorialUser")
    public void remove() {
        contents = null;
    }
}

The @RolesAllowed annotation is specified on methods for which you want to restrict access. In this example, only users in the role of TutorialUser will be allowed to add and remove books from the cart and to list the contents of the cart. A @RolesAllowed annotation implicitly declares a role that will be referenced in the application; therefore, no @DeclareRoles annotation is required. The presence of the @RolesAllowed annotation also implicitly declares that authentication will be required for a user to access these methods. If no authentication method is specified in the deployment descriptor, the type of authentication will be user name/password authentication.

To Run the cart-secure Example Using NetBeans IDE

  1. Follow the steps in To Set Up Your System for Running the Security Examples.

  2. From the File menu, choose Open Project.

  3. In the Open Project dialog box, navigate to:

    jakartaee-examples/tutorial/security
  4. Select the cart-secure folder.

  5. Select the Open Required Projects check box.

  6. Click Open Project.

  7. In the Projects tab, right-click the cart-secure project and select Build.

    This step builds and packages the application into cart-secure.ear, located in the cart-secure-ear/target/ directory, and deploys this EAR file to your GlassFish Server instance, retrieves the client stubs, and runs the client.

  8. In the Login for user: dialog box, enter the user name and password of a file realm user created in GlassFish Server and assigned to the group TutorialUser; then click OK.

    If the user name and password you enter are authenticated, the output of the application client appears in the Output tab:

    ...
    Retrieving book title from cart: Infinite Jest
    Retrieving book title from cart: Bel Canto
    Retrieving book title from cart: Kafka on the Shore
    Removing "Gravity's Rainbow" from cart.
    Caught a BookException: "Gravity's Rainbow" not in cart.
    Java Result: 1
    ...

    If the user name and password are not authenticated, the dialog box reappears until you enter correct values.

To Run the cart-secure Example Using Maven

  1. Follow the steps in To Set Up Your System for Running the Security Examples.

  2. In a terminal window, go to:

    jakartaee-examples/tutorial/security/cart-secure/
  3. To build the application, package it into an EAR file in the cart-secure-ear/target subdirectory, deploy it, and run it, enter the following command at the terminal window or command prompt:

    mvn install
  4. In the Login for user: dialog box, enter the user name and password of a file realm user created in GlassFish Server and assigned to the group TutorialUser; then click OK.

    If the user name and password you enter are authenticated, the output of the application client appears in the Output tab:

    ...
    Retrieving book title from cart: Infinite Jest
    Retrieving book title from cart: Bel Canto
    Retrieving book title from cart: Kafka on the Shore
    Removing "Gravity's Rainbow" from cart.
    Caught a BookException: "Gravity's Rainbow" not in cart.
    Java Result: 1
    ...

    If the user name and password are not authenticated, the dialog box reappears until you enter correct values.

The converter-secure Example: Securing an Enterprise Bean with Programmatic Security

This example demonstrates how to use the getCallerPrincipal and isCallerInRole methods with an enterprise bean. This example starts with a very simple enterprise bean application, converter, and modifies the methods of the ConverterBean so that currency conversion will occur only when the requester is in the role of TutorialUser.

This example can be found in the jakartaee-examples/tutorial/security/converter-secure directory. This example is based on the unsecured enterprise bean application, converter, which is discussed in Getting Started with Enterprise Beans and is found in the _jakartaee-examples/tutorial/ejb/converter/ directory. This section builds on the example by adding the necessary elements to secure the application by using the getCallerPrincipal and isCallerInRole methods, which are discussed in more detail in Securing an Enterprise Bean Programmatically.

In general, the following steps are necessary when using the getCallerPrincipal and isCallerInRole methods with an enterprise bean. In the example application included with this tutorial, many of these steps have been completed for you and are listed here simply to show what needs to be done should you wish to create a similar application.

  1. Create a simple enterprise bean application.

  2. Set up a user on GlassFish Server in the file realm, in the group TutorialUser, and set up default principal to role mapping. To do this, follow the steps in To Set Up Your System for Running the Security Examples.

  3. Modify the bean to add the getCallerPrincipal and isCallerInRole methods.

  4. If the application contains a web client that is a servlet, specify security for the servlet, as described in Specifying Security for Basic Authentication Using Annotations.

  5. Build, package, deploy, and run the application.

Modifying ConverterBean

The source code for the original ConverterBean class was modified to add the if..else clause that tests whether the caller is in the role of TutorialUser. If the user is in the correct role, the currency conversion is computed and displayed. If the user is not in the correct role, the computation is not performed, and the application displays the result as 0. The code example can be found in jakartaee-examples/tutorial/security/converter-secure/converter-secure-ejb/src/main/java/jakarta/tutorial/converter/ejb/ConverterBean.java.

The code snippet (with modifications shown in bold) is as follows:

package ee.jakarta.tutorial.convertersecure.ejb;

import java.math.BigDecimal;
import java.security.Principal;

import jakarta.annotation.Resource;
import jakarta.annotation.security.DeclareRoles;
import jakarta.annotation.security.RolesAllowed;
import jakarta.ejb.SessionContext;
import jakarta.ejb.Stateless;

@Stateless()
@DeclareRoles("TutorialUser")
public class ConverterBean{

    @Resource SessionContext ctx;
    private final BigDecimal yenRate = new BigDecimal("104.34");
    private final BigDecimal euroRate = new BigDecimal("0.007");

    @RolesAllowed("TutorialUser")
     public BigDecimal dollarToYen(BigDecimal dollars) {
        BigDecimal result = new BigDecimal("0.0");
        Principal callerPrincipal = ctx.getCallerPrincipal();
        if (ctx.isCallerInRole("TutorialUser")) {
            result = dollars.multiply(yenRate);
            return result.setScale(2, BigDecimal.ROUND_UP);
        } else {
            return result.setScale(2, BigDecimal.ROUND_UP);
        }
    }

    @RolesAllowed("TutorialUser")
    public BigDecimal yenToEuro(BigDecimal yen) {
        BigDecimal result = new BigDecimal("0.0");
        Principal callerPrincipal = ctx.getCallerPrincipal();
        if (ctx.isCallerInRole("TutorialUser")) {
             result = yen.multiply(euroRate);
             return result.setScale(2, BigDecimal.ROUND_UP);
        } else {
             return result.setScale(2, BigDecimal.ROUND_UP);
        }
    }
}

Modifying ConverterServlet

The following annotations specify security for the converter web client, ConverterServlet:

@WebServlet(urlPatterns = {"/"})
@ServletSecurity(
@HttpConstraint(transportGuarantee = TransportGuarantee.CONFIDENTIAL,
    rolesAllowed = {"TutorialUser"}))

To Run the converter-secure Example Using NetBeans IDE

  1. Follow the steps in To Set Up Your System for Running the Security Examples.

  2. From the File menu, choose Open Project.

  3. In the Open Project dialog box, navigate to:

    jakartaee-examples/tutorial/security
  4. Select the converter-secure folder.

  5. Click Open Project.

  6. Right-click the converter-secure project and select Build.

    This command builds and deploys the example application to your GlassFish Server instance.

To Run the converter-secure Example Using Maven

  1. Follow the steps in To Set Up Your System for Running the Security Examples.

  2. In a terminal window, go to:

    jakartaee-examples/tutorial/security/converter-secure/
  3. Enter the following command:

    mvn install

    This command builds and packages the application into a WAR file, converter-secure.war, that is located in the target directory, and deploys the WAR file.

To Run the converter-secure Example

  1. Open a web browser to the following URL:

    http://localhost:8080/converter-secure

    An Authentication Required dialog box appears.

  2. Enter a user name and password combination that corresponds to a user who has already been created in the file realm of GlassFish Server and has been assigned to the group TutorialUser; then click OK.

  3. Enter 100 in the input field and click Submit.

    A second page appears, showing the converted values.