Java Actionpack: How to protect actions
A very common issue dealing with web applications is how to protect a set of URLs so that only people who have the proper permissions can have access. With Java Actionpack, you would accomplish this through the use of filters. Filters can be called before or after actions, and you can be picky on which actions are supposed to be affected by those filters. Java Actionpack uses annotations to mark which methods on a controller are filters. All other public methods are actions. First, let’s look at the annotation we use:
@Filter(position = {Position}, rules = {Rules}, actions = [])
A closer look at the parameters:
positionPosition.BEFORE- The filter is called before the actions.
Position.AFTER- The filter is called after the actions.
rulesRules.ALL- The filter is called for all actions in the controller, no matter what the
actionslist parameter says. Rules.INCLUDE- The filter is called for only the actions listed in the
actionslist parameter. Rules.EXCLUDE- The filter is called for all actions not listed in the
actionslist parameter.
actions- The list of action names (method names in the controller) that this filter may affect depending on the rules. The default value is an empty list, so it can be omitted when you use
Rules.ALL
The first thing we need to worry about is how many of the actions need to be protected. Is the controller for an administration section where we would need to protect everything? Is the controller for general browsing, but we only need to protect just a couple of methods? Or possibly the controller is for the user self-management so we would need to protect everything except for the login and self registration actions. The only difference is in how we set up the filter. Just to keep things simple, let’s assume the controller is for an administration page.
public class AdminController extends Controller {
public static final String ADMIN_ROLE = "ADMINISTRATOR"
@Filter(position = Position.BEFORE, rules = Rules.ALL)
public void ensureUserIsAdmin() {
if ( ! request.isUserInRole(ADMIN_ROLE) ) {
sendMessage(
"You don't have permissions to access the administration pages.");
if ( null == request.getRemoteUser() ) {
request.setAttribute(ActionPack.CONTINUE, getThisUrl());
redirectToAction("security", "login");
} else {
redirectToAction("home", "index");
}
}
}
}
There’s a lot going on here. First, we have to check if the user is in the administration role. Once we know that, we can decide what to do. First, we send a message (this message is identified by a request attribute that Actionpack will forward through redirects). Next we determine whether the user is logged in or not. If the user is logged in we redirect them to the the action for HomeController.index. If the user is not logged in, we redirect them to the login page, with another special forwarding attribute so that the login section knows to come back to the original request. Don’t worry, the credentials will be checked again after a successful login.
So what’s the deal with request.isUserInRole() ? It’s a standard method on the HttpServletRequest object, and even if you don’t use container authentication, you can still use it in your application. Java Actionpack provides an interface that you use so that it knows what to do with your User object. The interface extends the java.security.Principle interface so that your User object is compatible with the request.getRemoteUser() and request.getUserPrinciple() methods. The user interface also extends java.io.Serializable so that it is compatible with session attributes in servlet engines that are compatible with the newest standards. Lastly, it provides one more method: isInRole(String) . The method is used so that the request wrapper can determine if the user object is in a the requested role.
So where do you put this object we created? The RequestWrapper looks in a session attribute identified by ActionPack.USER, which is the fully qualified class name for the User interface. So your login code will need to create an object that implements actionpack’s User interface, and place it in the session attribute ActionPack.USER and everything works as expected.
