I would like to secure my services layer using Spring Security. As explained in the documentation, I need to use a MethodSecurityInterceptor
that will check if
I achieved that by implementing my own AccessDecisionManager
that delegates access decisions to my special interface AccessDecisionStrategy
:
public interface AccessDecisionStrategy {
void decide(Authentication authentication, MethodInvocation methodInvocation, ConfigAttribute configAttribute);
}
Each access decision strategy represents different way of making access decision.
You can easily implement your own strategy (even in other language - for instance Scala):
public class SomeStrategy implements AccessDecisionStrategy { ...
As you can see, my AccessDecisionManager
has a map of strategies. Strategy used by manager is based on annotation argument.
public class MethodSecurityAccessDecisionManager implements AccessDecisionManager {
private Map strategyMap;
public MethodSecurityAccessDecisionManager(Map strategyMap) {
this.strategyMap = strategyMap;
}
@Override
public void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
ConfigAttribute configAttribute = getSingleConfigAttribute(configAttributes);
AccessDecisionStrategy accessDecisionStrategy = strategyMap.get(configAttribute.getAttribute());
if (accessDecisionStrategy == null) {
throw new IllegalStateException("AccessDecisionStrategy with name "
+ configAttribute.getAttribute() + " was not found!");
}
try {
accessDecisionStrategy.decide(authentication, (MethodInvocation) object, configAttribute);
} catch (ClassCastException e) {
throw new IllegalStateException();
}
}
private ConfigAttribute getSingleConfigAttribute(Collection configAttributes) {
if (configAttributes == null || configAttributes.size() != 1) {
throw new IllegalStateException("Invalid config attribute configuration");
}
return configAttributes.iterator().next();
}
@Override
public boolean supports(ConfigAttribute attribute) {
return true;
}
@Override
public boolean supports(Class> clazz) {
return clazz.equals(MethodInvocation.class);
}
}
Now when I want to protect my method I put @Secured
annotation with argument that is name of the strategy:
@Secured("GetByOwner")
FlightSpotting getFlightSpotting(Long id);
You can implement and configure as many strategies as you want:
To inject that access decision manager you type:
I also implemented helper class to handle MethodInvocation
arguments:
import org.aopalliance.intercept.MethodInvocation;
public class MethodInvocationExtractor {
private MethodInvocation methodInvocation;
public MethodInvocationExtractor(MethodInvocation methodInvocation) {
this.methodInvocation = methodInvocation;
}
public ArgumentType getArg(int num) {
try {
Object[] arguments = methodInvocation.getArguments();
return (ArgumentType) arguments[num];
} catch (ClassCastException | ArrayIndexOutOfBoundsException e) {
throw new IllegalStateException();
}
}
}
Now you can easily extract interesting arguments in the code of your strategy to make decision:
Let's say I want to get argument number 0
that is of type Long
:
MethodInvocationExtractor extractor = new MethodInvocationExtractor<>(methodInvocation);
Long id = extractor.getArg(0);