An Introduction to Aspect-Oriented programming with JBoss AOP
Externalising Security Checks
Logging is the common example used for introductions to AOP, so let's try doing something more interesting. Say we want to make sure that only users with the correct permissions can call a method. We could annotate our methods from BankAccount as follows:package bank;
public class BankAccount
{
int accountNumber;
int balance;
@Roles(roles= {"admin"})
public BankAccount(int accountNumber)
{
System.out.println("*** Bank Account constructor");
this.accountNumber = accountNumber;
}
...
@Roles(roles= {"admin"})
public void debit(int amount)
{
System.out.println("*** BankAccount.debit()");
balance -= amount;
}
@Roles(roles= {"admin", "user"})
public void credit(int amount)
{
System.out.println("*** BankAccount.credit()");
balance += amount;
}
}
So only users with the role “admin” can create BankAccount instances and debit accounts, while users with the role “admin” or “user” can credit accounts. We have created a security.properties file to configure users and their roles:
admin=password;admin,userThere is a user called 'admin' whose password is 'password' who has the roles 'admin' and 'user', and a user called 'guest' whose password is 'password' who only has the role 'user'.
guest=password;user
We can then apply a SecurityAspect to the methods annotated with the @Roles annotation. Note that like everything else in the pointcut language the annotations need to be fully qualified. The “..” in place of the parameters in the pointcut expressions means we want this aspect to be applied to all constructors and methods annotated with @Roles regardless of the parameters it takes.
<aop>
<aspect class="bank.SecurityAspect"/>
<bind pointcut="execution(bank.BankAccount->@bank.Roles(..))">
<around aspect="bank.SecurityAspect" name="checkSecurity"/>
</bind>
<bind pointcut="execution(* bank.BankAccount->@bank.Roles(..))">
<around aspect="bank.SecurityAspect" name="checkSecurity"/>
</bind>
</aop>
The SecurityAspect then checks that the correct user is used:
package bank;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import org.jboss.aop.joinpoint.Invocation;
public class SecurityAspect
{
Map<String, String> usernamePassword = new HashMap<String, String>();
Map<String, List<String>> userRoles = new HashMap<String, List<String>>();
public SecurityAspect() throws FileNotFoundException, IOException, URISyntaxException
{
//Initialise the usernamePassword and userRoles maps
//with information from security.properties
}
public Object checkSecurity(Invocation invocation) throws Throwable
{
String username = LoginInfo.getUsername();
String password = usernamePassword.get(username);
if (password == null)
{
throw new SecurityException("Unknown user");
}
if (!password.equals(LoginInfo.getPassword()))
{
throw new SecurityException("Wrong password");
}
Roles rolesAnnotaton = (Roles)invocation.resolveAnnotation(Roles.class);
List<String> hasRoles = userRoles.get(username);
boolean hasRole = false;
if (hasRoles != null)
{
for (String role : rolesAnnotaton.roles())
{
if (hasRoles.contains(role))
{
hasRole = true;
break;
}
}
}
if (!hasRole)
{
throw new SecurityException("Wrong roles for user");
}
return invocation.invokeNext();
}
}
The roles needed to invoke the target joinpoint are got from the invocation using this call:
Roles rolesAnnotaton = (Roles)invocation.resolveAnnotation(Roles.class);
This does the same as calling java.lang.reflect.Method.getAnnotation() or java.lang.reflect.Constructor.getAnnotation() for the called method or constructor, but also allows for annotation overrides as part of the aop configuration, which are beyond the scope of this article. Although the type of invocation used in this example is Invocation, the specific type created by JBoss AOP will be ConstructorInvocation or MethodInvocation depending on what we are calling. The referenced LoginInfo class is just a wrapper around some static fields containing the username and password.
package bank;
public class LoginInfo
{
private static String username;
private static String password;
public static void setUsernameAndPassword(String username, String password)
{
LoginInfo.username = username;
LoginInfo.password = password;
}
public static String getUsername()
{
return username;
}
public static String getPassword()
{
return password;
}
}
Now let us modify the Bank.main() method to populate the LoginInfo fields:
public static void main(String[] args)
{
System.out.println("*** Log in as 'guest' - it does not have the correct roles to create an account");
LoginInfo.setUsernameAndPassword("guest", "password");
System.out.println("*** Attempting to create account 1");
try
{
BankAccount acc1 = new BankAccount(1);
acc1.credit(150);
bankAccounts.put(acc1.getAccountNumber(), acc1);
}
catch(SecurityException e)
{
System.out.println("!!! Expected SecurityException " + e.getMessage());
}
System.out.println("*** Log in as 'admin' - the roles are fine for the rest now");
LoginInfo.setUsernameAndPassword("admin", "password");
System.out.println("*** Creating account 1");
BankAccount acc1 = new BankAccount(1);
acc1.credit(150);
bankAccounts.put(acc1.getAccountNumber(), acc1);
System.out.println("*** Creating account 2");
BankAccount acc2 = new BankAccount(2);
acc2.credit(230);
bankAccounts.put(acc2.getAccountNumber(), acc2);
System.out.println("*** Balance acount 1: " + acc1.getBalance());
System.out.println("*** Balance acount 2: " + acc2.getBalance());
//Transfer some money
System.out.println("*** Transfer 50 from account 1 to account 2");
transfer(acc1, acc2, 50);
System.out.println("*** Balance acount 1: " + acc1.getBalance());
System.out.println("*** Balance acount 2: " + acc2.getBalance());
}
We first try to create a BankAccount with a user who does not have the required admin role, and then we do the rest, as before, with an user with the required roles. The output of running this application is:
*** Log in as 'guest' - it does not have the correct roles to create an account
*** Attempting to create account 1
!!! Expected SecurityException Wrong roles for user
*** Log in as 'admin' - the roles are fine for the rest now
*** Creating account 1
*** Bank Account constructor
*** BankAccount.credit()
*** Creating account 2
*** Bank Account constructor
*** BankAccount.credit()
*** Balance acount 1: 150
*** Balance acount 2: 230
*** Transfer 50 from account 1 to account 2
*** BankAccount.debit()
*** BankAccount.credit()
*** Balance acount 1: 100
*** Balance acount 2: 280
By using AOP to apply security, we have extracted the security checks into one place in our application, and used annotations to configure that. If we wanted to use a different mechanism of configuring the users we could leave the core application the same, write another aspect and easily change how we use security everywhere by modifying the jboss-aop.xml file.
The code for this example can be found in the listing3/ folder of the download bundle.
| Attachment | Size |
|---|---|
| jboss-aop-samples.zip | 4.76 MB |
- Login or register to post comments
- 21936 reads
- Printer-friendly version
(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)










Comments
Joshua Partogi replied on Fri, 2008/08/29 - 10:05pm
jaikiran replied on Sat, 2008/08/30 - 3:52am
Good article, Kabir.
I have a question. Is the aspect code allowed to change the value that is being passed to the joinpoint? I mean, if the credi(int amount) method was being passed a value of 200, can the code in the aspect change this value to 100 before the credit method is invoked? If yes, is there any way to secure such access?
Kabir Khan replied on Mon, 2008/09/01 - 5:06am
Joshua,
Yes annotations can be used instead.An example can be found here and the containing directory contains more examples. If using annotations, instead of passing in the path of a jboss-aop.xml file with -Djboss.aop.path, you point JBoss AOP to a directory containing your annotated aspects using -Djboss.aop.class.path. The tutorial examples that come with the download will show how in more detail.
Kabir Khan replied on Mon, 2008/09/01 - 5:13am
Jaikiran,
The aspect code can modify the values that are passed in. There is currently no way to stop this, if that is what you mean by securing it. If this feature is important to you please ask for it on our user forums, and we will take it into consideration.
gsowji replied on Tue, 2009/02/10 - 11:37pm
gsowji replied on Wed, 2009/02/11 - 1:18am
daveeeed replied on Fri, 2009/06/05 - 2:54am
warrenty replied on Mon, 2009/06/08 - 1:40am
modthoa replied on Wed, 2009/06/17 - 10:51am
emad964 replied on Mon, 2009/06/29 - 4:14pm
jiji530 replied on Mon, 2009/06/29 - 9:42pm
wikaniko replied on Thu, 2009/07/09 - 1:02pm
superpan3721 replied on Sat, 2009/08/01 - 1:38am
nakul replied on Tue, 2009/10/20 - 1:49pm
in response to: thejavafreak
markgrant1st replied on Wed, 2009/11/04 - 2:27am
Wish I could learn the programming . I know each and everything about the Markets but computer, me and computer can not be friends at all :)\
Home Insurance Rates
Teddy P replied on Thu, 2009/11/05 - 9:46am
abcdentist replied on Sun, 2009/11/08 - 2:57am
hpmedia replied on Sun, 2009/11/08 - 8:44pm
hpmedia replied on Tue, 2009/11/17 - 5:40pm