Secure Your Method Using AOP
In this article, we take a look at how you can secure your methods using aspect-oriented programming.
Join the DZone community and get the full member experience.
Join For FreeIn this article we learn how to secure our methods the easy way!
We basically use the Before Advice of aspect-oriented programming (AOP) to achieve our goal. The article even illustrates how easy it is to use AOP to implement a crosscutting concern such as security. Let's get started
Pre-requisites:
- Knowledge of Spring Framework.
- Overview of AOP
What is AOP?
Spring AOP enables Aspect-Oriented Programming in Spring applications. In AOP, aspects enable the modularization of concerns such as transaction management, logging, or security that cut across multiple types and objects (often called crosscutting concerns).
What Is an Advice?
Advice is an action taken by an aspect at a particular join point. Different types of advice include “around,” “before” and “after” advice.
What is Before advice?
Before advice: Advice that executes before a join point, but which does not have the ability to prevent execution flow proceeding to the join point (unless it throws an exception).
The following code snippet shows the SecureMessage
class. This is the class that we will be securing using AOP.
x
public class SecureMessage
{
public void writeSecureMessage()
{
System.out.println("100 pushup and 100 situp is the key to success-Saitama");
}
}
Because this example requires users to authenticate, we are going to need to store their details. The following code snippet shows the UserInfo
class we can use to store a user’s credentials:
x
public class UserInfo
{
private String userName;
private String password;
public UserInfo(String userName, String password)
{
this.userName = userName;
this.password = password;
}
public String getPassword() {
return password;
}
public String getUserName() {
return userName;
}
}
}
This class simply holds data about the user so that we can use it to validate the user. The following code snippet shows the SecurityManager
class, which is responsible for authenticating users and storing their credentials for later retrieval:
xxxxxxxxxx
public class SecurityManager {
private static ThreadLocal<UserInfo>
threadLocal = new ThreadLocal<>();
public void login(String userName, String password) {
threadLocal.set(new UserInfo(userName, password));
}
public void logout() {
threadLocal.set(null);
}
public UserInfo getLoggedOnUser() {
return threadLocal.get();
}
}
Please note, in a real application, the login()
method would probably check the supplied credentials against a database or LDAP directory, but here we check and assign against static values.
- login(): method creates a
UserInfo
object for the user and stores it on the current thread by using ThreadLocal. - logout(): method sets any value that might be stored in
ThreadLocal
to null. - getLoggedOnUser(): method returns the
UserInfo
object for the currently authenticated user. This method returns null if no user is authenticated.
Now we get to the interesting stuff.
To check whether a user is authenticated and, if so, whether the user is permitted to access the methods on SecureMessage, we need to create advice that executes before the method and checks the UserInfo object returned by SecurityManager.getLoggedOnUser() against the set of credentials for allowed users. The code for this advice, SecurityAdvice, is shown as follows:
x
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class SecurityAdvice implements MethodBeforeAdvice {
private SecurityManager securityManager;
public SecurityAdvice() {
this.securityManager = new SecurityManager();
}
public void before(Method method, Object[] args, Object target)
throws Throwable {
UserInfo user = securityManager.getLoggedOnUser();
if (user == null) {
System.out.println("No user authenticated");
throw new SecurityException(
"You must login before attempting to invoke the method: "
+ method.getName());
} else if ("Saitama".equals(user.getUserName())) {
System.out.println("Logged in user is Saitama - OKAY!");
} else {
System.out.println("Logged in user is " + user.getUserName()
+ " NOT GOOD :(");
throw new SecurityException("User " + user.getUserName()
+ " is not allowed access to method " + method.getName());
}
}
before() method, we perform a simple check to see whether the username of the authenticated user is Saitama. If so, we allow the user access; otherwise, an exception is raised. Also notice that we check for a null UserInfo object, which indicates that the current user is not authenticated.
In the following code snippet, you can see a sample application that uses the SecurityAdvice class to secure the SecureMessage class:
x
import org.springframework.aop.framework.ProxyFactory;
public class SecurityDemo {
public static void main(String... args) {
SecurityManager mgr = new SecurityManager();
SecureMessage bean = getSecureBean();
mgr.login("Saitama", "pwd");
System.out.println("---Scenario 1---");
bean.writeSecureMessage();
mgr.logout();
try {
mgr.login("invalid user", "pwd");
System.out.println("---Scenario 2---");
bean.writeSecureMessage();
} catch (SecurityException ex) {
System.out.println("Exception Caught: " + ex.getMessage());
} finally {
mgr.logout();
}
try {
System.out.println("---Scenario 3---");
bean.writeSecureMessage();
} catch (SecurityException ex) {
System.out.println("Exception Caught: " + ex.getMessage());
}
}
private static SecureBean getSecureBean() {
SecureMessage target = new SecureMessage();
SecurityAdvice advice = new SecurityAdvice();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvice(advice);
SecureMessage proxy = (SecureMessage) factory.getProxy();
return proxy;
}
}
Output:
---Scenario 1---
Logged in user is Saitama - OKAY!
100 pushup and 100 situp is the key to success-Saitama
---Scenario 2---
Logged in user is invalid user NOT GOOD.
Exception Caught: User invalid user is not allowed access to method writeSecureMessage
---Scenario 3---
No user authenticated
Exception Caught: You must login before attempting to invoke the method: writeSecureMessage
Explanation
In the getSecureBean()
method, we create a proxy of the SecureMessage
class that is advised using an instance of SecurityAdvice. This proxy is returned to the caller. When the caller invokes any method on this proxy, the call is first routed to the instance of SecurityAdvice for a security check. In the main()
method, we test three scenarios, invoking the SecureMessage.writeSecureMessage()
method with two sets of user credentials and then no user credentials at all. Because SecurityAdvice
allows method calls to proceed only if the currently authenticated user is Saitama, we can expect that the only successful scenario in the previous code is the first.
As you can see, only the first invocation of SecureMessage.writeSecureMessage()
was allowed to proceed. The remaining invocations were prevented by the SecurityException
exception thrown by SecurityAdvice
. This example is simple, but it does highlight the usefulness of before advice. Security is a typical example of before advice, but we also find it useful when a scenario demands the modification of arguments passed to the method.ALU
Opinions expressed by DZone contributors are their own.
Comments