Securing Stripes With ACLs
Here is the solution I pursued when I needed to add security to Stripes. Since Stripes uses annotations and strays from the traditional xml configuration, I thought it would be nice to have an annotation based solution to securing actions. The solution I came up with allows the user to apply role based security at both the class and method level of stripes actions. This information is particularly useful for those wanting an ACL based system or tying into legacy systems using ACL based security. The first step is to get the stripes-security source which you can download below.
This new new version combines the tag and security mechanisms. It also changed where the security interceptor runs to the LifecycleStage.HandlerResolution so the action bean is available to AutoExceptionHandlers. The jsp tag now utilizes the Stripes Security Manager by default so you don't have to code your own. I figured if I was going to put this out here, I may as well do it right and make it easy. I have added a new method to the SecurityManager
public boolean isUserInRole(List<String> roles, HttpServletRequest request, HttpServletResponse response);
because as far as I know, the tag doesn't have access to the context. I also left the original method there for backwards compatibility. This version 1.4.2 is the latest one. You can include the new taglib using the following.
<%@ taglib uri="http://stripes.sourceforge.net/stripes-security.tld" prefix="ss"%>
You can download the latest source code(1.4.2) for the combined security add on
here
Here are the old jars in case anybody has some use for them. They are now considered deprecated.
You can download the previous source code for the security add on []
You can download the previous source code for the security jsp tag here
Next, you will need to set up the web.xml to use the StripesSecurityFilter instead of the standard StripesFilter. You should still be able to do everything you could with the stripes filter since this is just an extension of that class.
<filter> <description> Provides essential configuration and request processing services for the Stripes framework. </description> <display-name>Stripes Security Filter</display-name> <filter-name>StripesSecurityFilter</filter-name> <filter-class>com.tess.security.controller.StripesContextFilter</filter-class> <init-param> <param-name>SecurityManager.Class</param-name> <param-value>com.tess.security.util.TessSecurityManager</param-value> </init-param> <init-param> <param-name>UnauthorizedResolutionURL</param-name> <param-value>/unauthorized.jsp</param-value> </init-param> <init-param> <param-name>Interceptor.Classes</param-name> <param-value> net.sourceforge.stripes.security.controller.SecurityInterceptor, net.sourceforge.stripes.controller.BeforeAfterMethodInterceptor</param-value> </init-param> </filter> <filter-mapping> <filter-name>StripesSecurityFilter</filter-name> <url-pattern>*.jsp</url-pattern> <dispatcher>REQUEST</dispatcher> </filter-mapping> <filter-mapping> <filter-name>StripesSecurityFilter</filter-name> <servlet-name>StripesDispatcher</servlet-name> <dispatcher>REQUEST</dispatcher> </filter-mapping>
The setup complies with the standard stripes setup. The SecurityManager and SecurityInterceptor are both required. The UnauthorizedResolutionURL is optional and is used to redirect the user to a custom page upon unauthorized access. If this isn't set, it will just throw a StripesAuthorizationException.
The SecurityManager is the custom piece as to managing your security. There are many ways to manage security so the SecurityManager was written as an interface which must be implemented using your own class and this is the class that is set using the SecurityManager.Class init parameter in the filter. An example security manager is below.
ExampleSecurityManager.java |
---|
import java.util.List; import com.tess.security.controller.StripesSecurityManager; import net.sourceforge.stripes.action.ActionBeanContext; public class MySecurityManager implements StripesSecurityManager { public boolean isUserInRole(List<String> roles, ActionBeanContext context) { // if (roles == null || roles.size() < 1) return false; for (String role : roles) { // Iterate through the roles and validate them in whatever manner you want. // If the user is unauthorized, return false. Otherwise return true if they // are authorized. if (notAuthorized) return false; } return true; } } |
The role list that is passed in is the role(s) that are set on the action using the @Secure annotation. These are just strings and it is up to you to check these roles against the user that is currently logged in to see if they are authorized to access this method or class within the action. The action context is passed in to allow full access to the request, session, etc. This should provide ample flexibility in allowing the developer to plug into any role management framework. A simple example action using the security is below.
ExampleAction.java |
---|
package example; import java.util.List; import net.sourceforge.stripes.action.ActionBeanContext; import net.sourceforge.stripes.action.DefaultHandler; import net.sourceforge.stripes.action.DontValidate; import net.sourceforge.stripes.action.ForwardResolution; import net.sourceforge.stripes.action.HandlesEvent; import net.sourceforge.stripes.action.Resolution; import net.sourceforge.stripes.action.UrlBinding; import net.sourceforge.stripes.validation.Validate; import net.sourceforge.stripes.validation.ValidateNestedProperties; // If a role is put on the class, you must have that role to access anything inside the class. // You don't need to have a Secure annotation here unless you want to require everyone that uses // this action to have this role. The class level annotation is processed before method level roles. @Secure(roles = "MANAGE_GROUPS") public class ManageGroupsAction implements ActionBean { String groupId; String companyId; @DefaultHandler public Resolution manageGroups() { return null; } @Secure(roles = "CREATE_GROUP") public Resolution createGroup() { return null; } @Secure(roles = "SAVE_GROUP") public Resolution saveGroup() { return null; } @Secure(roles="DELETE_GROUP") public Resolution deleteGroup() { return null; } @Secure(roles="SAVE_GROUP") public Resolution editGroup() { return null; } // This method by default requires the role that is over the class. public Resolution viewGroups() { return null; } } |
Roles can be applied at either the class or method level. Roles are string based and separated by commas to apply more than 1 role to a method. Checking always starts at the class level and then goes to the method level. For example, if you have "MANAGE_GROUPS" on the class and "DELETE_GROUP" on the method, the user would be required to have both roles to delete a group. You don't need any roles at the class level so you can control everything at the method or you can have a role(s) at the class and none on the methods to lock down the entire class. A new feature has been added to allow for annotations to be inherited from a parent class. Now you can create your own base action for instance and add a @Secure annotation at the class level. All children will automatically inherit this level of security until that child provides its own @Secure annotation at the class level which will override the parents permissions. This is only implemented on the class level and doesn't apply at the method level. I felt this provided enough flexibility (for me anyway) without becoming overly complex.
One other piece I neglected to add in here was the code for a convenient little jsp tag to allow for securing pieces of a jsp. Below is an example of how it is used. You will need to define your own tld.
<%@ taglib uri="http://stripes.sourceforge.net/stripes-security.tld" prefix="tess"%>
<ss:secure roles="ADMIN, POWER_USER">
Secured content goes in here
</ss:secure>
You can use a comma separated list of roles and it works much the same way as the stripes security. If you have the role that is listed, it allows the code
between the tags to be executed. You download the base source code and tld using the link at the top of the page.
Please let me know if you have suggestions, find bugs or whatever. If I have forgotten anything, please let me know. I can usually be reached through the stripes users mailing list.
Thanks
Nic