Programmatic security is based upon the Java Authentication and Authorization Service (JAAS) API. It should be used when declarative annotation is not adequate to affect the level of security desired. This can occur when access is time-based. For example, a user may only be allowed to access certain services during normal business hours such as when the stock market is open.
Programmatic security is affected by adding code within methods to determine who the caller is and then allowing certain actions to be performed based on their capabilities. There are two EJBContext
interface methods available to support this type of security: getCallerPrincipal
and isCallerInRole
. The SessionContext
object implements the EJBContext
interface. The SessionContext's getCallerPrincipal
method returns a Principal
object which can be used to get the name or other attributes of the user. The isCallerInRole
method takes a string representing a role and returns a Boolean value indicating whether the caller of the method is a member of the role or not.
The steps for controlling security programmatically involve:
SessionContext
instanceTo demonstrate these two methods we will modify the SecurityServlet
to use the VoucherManager's approve
method and then augment the approve
method with code using these methods.
First modify the SecurityServlet
try block to use the following code. We create a voucher as usual and then follow with a call to the submit
and approve
methods.
out.println("<html>"); out.println("<head>"); out.println("<title>Servlet SecurityServlet</title>"); out.println("</head>"); out.println("<body>"); voucherManager.createVoucher("Susan Billings", "SanFrancisco", BigDecimal.valueOf(2150.75)); voucherManager.submit(); boolean voucherApproved = voucherManager.approve(); if(voucherApproved) { out.println("<h3>Voucher was approved</h3>"); } else { out.println("<h3>Voucher was not approved</h3>"); } out.println("<h3>Voucher name: " + voucherManager.getName() + "</h3>"); out.println("</body>"); out.println("</html>");
Next, modify the VoucherManager
EJB by injecting a SessionContext
object using the @Resource annotation.
public class VoucherManager { ... @Resource private SessionContext sessionContext;
Let's look at the getCallerPrincipal
method first. This method returns a Principal
object (java.security.Principal) which has only one method of immediate interest: getName
. This method returns the name of the principal.
Modify the approve
method so it uses the SessionContext
object to get the Principal
and then determines if the name of the principal is "mary" or not. If it is, then approve the voucher.
public boolean approve() { Principal principal = sessionContext.getCallerPrincipal(); System.out.println("Principal: " + principal.getName()); if("mary".equals(principal.getName())) { voucher.setApproved(true); System.out.println("approve method returned true"); return true; } else { System.out.println("approve method returned false"); return false; } }
Execute the SecurityApplication
using "mary" as the user. The application should approve the voucher with the output as shown in the following screenshot:
Execute the application again with a user of "sally". This execution will result in an exception.
INFO: Access exception
The getCallerPrincipal
method simply returns the principal. This frequently results in the need to explicitly include the name of a user in code. The hard coding of user names is not recommended. Checking against each individual user can be time consuming. It is more efficient to check to see if a user is in a role.
The isCallerInRole
method allows us to determine whether the user is in a particular role or not. It returns a Boolean value indicating whether the user is in the role specified by the method's string argument. Rewrite the approve
method to call the isCallerInRole
method and pass the string "manager" to it. If the return value returns true
, approve the voucher.
public boolean approve() { if(sessionContext.isCallerInRole("manager")) { voucher.setApproved(true); System.out.println("approve method returned true"); return true; } else { System.out.println("approve method returned false"); return false; } }
Execute the application using both "mary" and "sally". The results of the application should be the same as the previous example where the getCallerPrincipal
method was used.
The SessionContext
class was used to obtain either a Principal
object or to determine whether a user was in a particular role or not. This required the injection of a SessionContext
instance and adding code to determine if the user was permitted to perform certain actions.
This approach resulted in more code than the declarative approach. However, it provided more flexibility in controlling access to the application. These techniques provided the developer with choices as to how to best meet the needs of the application.
It is possible to take different actions depending on the user's role using the isCallerInRole
method. Let's assume we are using programmatic security with multiple roles.
@DeclareRoles ({"employee", "manager","auditor"})
We can use a validateAllowance
method to accept a travel allowance amount and determine whether it is appropriate based on the role of the user.
public boolean validateAllowance(BigDecimal allowance) { if(sessionContext.isCallerInRole("manager")) { if(allowance.compareTo(BigDecimal.valueOf(2500)) <= 0) { return true; } else { return false; } } else if(sessionContext.isCallerInRole("employee")) { if(allowance.compareTo(BigDecimal.valueOf(1500)) <= 0) { return true; } else { return false; } } else if(sessionContext.isCallerInRole("auditor")) { if(allowance.compareTo(BigDecimal.valueOf(1000)) <= 0) { return true; } else { return false; } } else { return false; } }
The compareTo
method compares two BigDecimal
values and returns one of three values:
The valueOf
static method converts a number to a BigDecimal
value. The value is then compared to allowance
. This data type is discussed in more detail in Chapter 12, How to support currency recipe.
18.118.12.50