Writing a custom role based access control framework that integrates with Struts

No Gravatar

This article will tell you a fairly simple way to put a custom access control framework in place for your J2EE application that will let you integrate it with the struts framework role based access control protection mechanisms. There are a few pieces to this solution:
1. A storage mechanism for the accesses (roles) per user
2. Writing these accesses into the HttpSession per user (generally in the form of an easily accessible object with helper functions on the session)
3. Override the processRoles method of the struts RequestProcessor class in order to recognize your custom roles
4. Add roles=”…” to your struts actions to protect them based on the configured roles

Ok, in order then, here we go.

For #1 – this is usually done via a relational database – whatever application database you’re using, you’ll generally store the accesses for a given user. However, you could store these in flat files, ldap, or some other mechanism if you wanted to – that’s up to you.

For #2 – here’s an object you can use to hold the roles.


public class UserAccesses {
    private String userName;
    private String[] roles;

    public UserAccesses(String userName, String[] roles) {
        this.userName = userName;
        this.roles = roles;
    }

    public boolean hasRole(String role) {
        if (roles.length > 0) {
            for (int i = 0; i < roles.length; i++) {
                if (role.equals(roles[i]))
                    return true;
            }
        }
        return false;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer(1024);
        sb.append(userName);
        sb.append(" has roles: ");
        for(int i=0; i

This object stores a string array of the role names per user. Now here's some code to write this object into the session. This code should be called when the user logs into the application.


    List allAccessesList = new ArrayList();
    //if the user has the write stories access from the db
    allAccessesList.add(Constants.WRITE_STORIES);
    //if the user has the read stories access from the db
    allAccessesList.add(Constants.READ_STORIES);
    //if the user has the delete stories access from the db
    allAccessesList.add(Constants.DELETE_STORIES);

    String[] allUserAccesses =
	(String[])allAccessesList.toArray(new String[0]);
    UserAccesses userAccessesObj =
	new UserAccesses (userId, allUserAccesses);
    HttpSession session = request.getSession();
    session.setAttribute(Constants.USER_SESSION_ACCESSES, userAccessesObj);

On to #3 - here you have to write a custom class that overrides the processRoles method of the RequestProcessor class in struts


public class RolesRequestProcessor extends RequestProcessor {
    private static final Logger log =
	LogFactory.getLog(RolesRequestProcessor.class);
    public RolesRequestProcessor() {
        super();
    }

    /**
     * @see org.apache.struts.action.RequestProcessor
     *		#processRoles(javax.servlet.http.HttpServletRequest,
     *      javax.servlet.http.HttpServletResponse,
     *      org.apache.struts.action.ActionMapping)
     */
    protected boolean processRoles(HttpServletRequest request,
		HttpServletResponse response, ActionMapping mapping)
        	throws IOException, ServletException {

        //Obtain the list of roles from action config
        String roles[] = mapping.getRoleNames();
        if ((roles == null) || (roles.length < 1)) {
            log.debug("No role is required for running this operation.");
            return (true);
        }
        //verify user possesses role
        StringBuffer sb =
		new StringBuffer("Check for required roles ....... n");

        //checking in ldap roles
        for (int i = 0; i < roles.length; i++) {
            sb.append(roles[i]);
            sb.append(", ");

            if (request.isUserInRole(roles[i])) {
                sb.append("Found a matching role in the ldap roles: " + roles[i]);
                return (true);
            }
        }

        // checking in custom database roles
        // Check the current user against the list of required roles
        HttpSession session = request.getSession();
        UserAccesses userAccessesObj = (UserAccesses )
        	session.getAttribute(Constants.USER_SESSION_ACCESSES);
        if (userAccessesObj == null) {
            return false;
        }
        for (int i = 0; i < roles.length; i++) {
            if (userAccessesObj .hasRole(roles[i])) {
                sb.append("Found a matching role in the custom roles: " + roles[i]);
                return (true);
            }
        }
        //end custom roles check

        //invoke declarative exception handling
        sb.append("n");
        sb.append("User does not have the required role");
        log.debug(sb.toString() + " ");
        ActionForward forward = null;
        try {
            forward = processException(request, response,
                new my.application.AuthorizationViolationException(),
		null, mapping);
        } catch (Throwable e) {
            e.printStackTrace();
        }
        // forward user to the authorization error page...
        if (forward != null) {
            log.info("Forward to authorization error page");
            processForwardConfig(request, response, forward);
        }
        return (false);
    }
}

Finally #4 - here you just add the roles attribute to your struts configuration in order to get struts to check for the specified role(s) (multiple roles, is just a comma-delimeted group) to allow a user to perform a given action.


    <action path="/myApplication/deleteStory"
		type="my.actions.DeleteStoryAction" roles="DELETE_STORIES,ADMIN">
        <forward name="deleteSuccess" path="/myApplication/deleteSuccess.do" />
    </action>

This process has worked very well on my projects, and hopefully will on yours as well.

Hope this helps.

Be Sociable, Share!

One thought on “Writing a custom role based access control framework that integrates with Struts

  1. Daniel,
    If you tell me what specifically you didn’t understand, I’ll try and clarify. Otherwise, hope this helps.
    Thanks,
    John

Leave a Reply

Your email address will not be published. Required fields are marked *