Dynamic Spring Security Sample

Introduction

We have seen in the previous article Dynamically Securing Method Execution with Spring Security how it is possible to exploit the nature of Spring Security ACL module to dinamically secure methods access. Here we detail this solution with a working example. The example source code is available in SpringDynamicSecurityExample. It is based on the Spring Security “contacts” sample that you can find in Spring Security Samples.

A short summary

We have seen that the Sid entity could represent a Principal or a GranthedAuthority. This is the crucial point that allows us to exploit the ACL itself to secure methods execution in a fully dynamic way. We recall here that every secured object in the ACL model is associated with one and only ACL entity.

The ACL entity can have multiple Access Control Entries which are represented by Permission, Sid and Acl instances. An ACE in which the Sid is a GrantedAuthority can be seen as a permission on an object granted to a Role, where the Role is the GrantedAuthority.

If our goal is to secure method execution (normally we would secure service public methods) then the object associated with the ACL would be a method and the permission would be related to its execution. So we can define a custom permission calling it ‘execute’ for instance.

The Acl would represent a method execution with its set of ACEs with the ‘execute’ permission granted to a user or role (i.e. to a Principal or GrantedAuthority.

The only thing we would have to do then is to define a PermissionEvaluator with a custom permission factory, and a custom voter. We will see below how we can implement a simple example.

Dynamic Spring Security Sample

The example runs with a HSQLDB database in memory. The DataSourcePopulator class initializes the db with all the ACL tables and records and creates two users, ’granted’ and ‘notGranted’. The first user is given the execution permission on the method ‘secure’ of the TestSecuredMethodService class. The second user does not have any permission.

The execution permission is implemented by the class CustomPermission:

package dynamicsecurity.methodsecurity;

import org.springframework.security.acls.domain.BasePermission;

public class CustomPermission extends BasePermission {

	public static final CustomPermission EXECUTE = 
              new CustomPermission(
			1 << 5, 'E');

	protected CustomPermission(int mask) {
		super(mask);
	}

	protected CustomPermission(int mask, char code) {
		super(mask, code);
	}
}

And we also have a custom permission factory that registers our custom permission:

package dynamicsecurity.methodsecurity;

import org.springframework.security.acls.domain.DefaultPermissionFactory;

public class CustomPermissionFactory extends DefaultPermissionFactory {
	public CustomPermissionFactory() {
		super();
		registerPublicPermissions(CustomPermission.class);
	}
}

The custom permission factory is configured in the file applicationContext-security.xml, where the permission evaluator is given our custom permission factory as the value for the “permissionFactory” property. In the same file the access decision manager is configured with a custom voter which is implemented as:

import java.util.Collection;

import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.acls.model.MutableAclService;
import org.springframework.security.core.Authentication;



public class CustomVoter implements AccessDecisionVoter {

	
    @Autowired
    private MutableAclService mutableAclService;
    
    @Autowired
    private PermissionEvaluator permissionEvaluator ;
	

	public boolean supports(ConfigAttribute attribute) {
		return true;
	}

	public boolean supports(Class<?> arg0) {
		return true;
	}

	public int vote(Authentication authentication, Object obj,
			Collection attributes) {
		
		if(object instanceof  ReflectiveMethodInvocation){
			MethodInvocation methodInvocation = 
(MethodInvocation) obj;			
			MethodWrapper methodWrapper = 
new MethodWrapper(methodInvocation.getMethod());	
			boolean haspermission = 
permissionEvaluator.hasPermission(authentication, 
     methodWrapper, CustomPermission.EXECUTE);					
			if (!haspermission) {
				return ACCESS_DENIED;
			}					
			
		}

		return ACCESS_GRANTED;		
	}

}

The vote(…) method checks first if the object passed as parameter is an instance of ReflectiveMethodInvocation. If this is the case it means that a method annotated with spring security @Secured, @preAuthorize or @postAuthorize is being executed. Since in our specific model we want to mark a method to be secured but without any reference to roles we choose to implement our custom annotation like the following:

package dynamicsecurity.methodsecurity;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.security.access.annotation.Secured;


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Secured("ROLE_DUMMY")
@interface SecureMethodExecution  {
   
}

This annotation uses the @Secured annotation as a meta-annotation and the “ROLE_DUMMY” string does not represent, as its name implies, a meaningful role, its only purpose is to make our SecureMethodExecution annotation recognized by Spring Security.Our vote(…) implementation uses the permission evaluator to check if the authentication object that represents the authenticated principal has the EXECUTE permission on the method that is being executed.The MethodWrapper class is a wrapper around the Method object retrieved from the ReflectiveMethodInvocation instance. Its purpose is to provide an ID by which it could be stored in ACL as a secured object. As you can see the constructor calculates an id using the object meta-information:

package dynamicsecurity.methodsecurity;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

public class MethodWrapper {


	private Method method;
	private int id;
	
	public MethodWrapper(Method method) {
		super();
		this.method = method;    
		Class<?>[] pType  = method.getParameterTypes();
		Type[] gpType = method.getGenericParameterTypes();
		String parTypes = "";
		for (int i = 0; i < pType.length; i++) {
			parTypes += "-" + pType[i];
		}
		for (int i = 0; i < gpType.length; i++) {
			parTypes += "-" + gpType[i];
		}
		String identifier = method.getDeclaringClass()
                .getName() + "." + method.getName() + "-" +  parTypes;	
	    int sum = 0;
	    for (char c : identifier.toCharArray()){
	    	sum += (int)c;	    	
	    }
	    this.id = sum;
	}
	


	public int getId() {
		return id;
	}
}

Here we see the TestSecuredMethodService in which the method secured is marked with @SecureMethodExecution while the notSecured() method as its name implies is not secured:

package dynamicsecurity.methodsecurity;


import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

@Component
@Service
public class TestSecuredMethodService {

	@SecureMethodExecution
	public String secured() {
		return "secured";
	}

	public String notSecured() {
		return "notSecured";
	}

}

These methods can be executed by a jsp page by the TestSecureMethodController:

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import dynamicsecurity.methodsecurity.TestSecuredMethodService;

@Controller
public class TestSecureMethodController {


	@Autowired
	private TestSecuredMethodService testSecuredMethodManager;



	@RequestMapping(value = "/secure/executeSecuredMethod.htm", 
           method = RequestMethod.GET)
	public ModelAndView executeSecuredMethod() {

		String result = testSecuredMethodManager.secured();
		Map<String, String> model = 
                  new HashMap<String, String>();
		model.put("TestSecuredMethodResult", result);
		return new ModelAndView("projectx/testSecureMethod", 
                  model);
	}
	
	
	@RequestMapping(value = "/secure/executeNotSecuredMethod.htm", 
method = RequestMethod.GET)
	public ModelAndView executeNotSecuredMethod() {

		String result = testSecuredMethodManager.notSecured();
		Map<String, String> model = 
                  new HashMap<String, String>();
		model.put("TestSecuredMethodResult", result);
		return new ModelAndView("projectx/testSecureMethod",
                   model);
	}
	
}

If we run the application we have the following initial page:

First

Clicking on “Test Method Security” we are required to login:

Second

We have two users “granted” and “notGranted” with password “user”. When we pass the login we are given the following page:

Third

The “Execute Secured Method” will execute the secured() method of the TestSecuredMethodService class and the “Execute Not Secured Method” the notSecured() one. If we login with “granted” user and click on the first link we will see the page below with “Method executed” message.

Fourth

If we login with the “notGranted” user we will see the following error page:

Fifth

If with any of the users we click on the second link we will have the “Method executed!” page because the “notSecured” method is not under security control, i.e. is not annotated with @SecureMethodExecution.

Dynamic Spring Security Sample last modified: 2015-07-13T00:47:23+00:00 by Mario Casari

2 thoughts on “Dynamic Spring Security Sample

Leave a Reply

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