I am trying to create a a custom method for use in Pre/Post Authorize calls like this:
public class CustomLSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler{
public CustomSecurityExpressionHandler(){
super();
}
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation){
CustomSecurityExpressionRoot root = new CustomSecurityExpressionRoot(authentication);
root.setThis(invocation.getThis());
root.setPermissionEvaluator(getPermissionEvaluator());
return root;
}
}
and
public class CustomSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
private Object filterObject;
private Object returnObject;
private Object target;
public CustomSecurityExpressionRoot(Authentication a) {
super(a);
}
public boolean testDecision(String test){
System.out.println("Printing:"+test+"\n");
return true;
}
public void setFilterObject(Object filterObject) {
this.filterObject = filterObject;
}
public Object getFilterObject() {
return filterObject;
}
public void setReturnObject(Object returnObject) {
this.returnObject = returnObject;
}
public Object getReturnObject() {
return returnObject;
}
void setThis(Object target) {
this.target = target;
}
public Object getThis() {
return target;
}
public boolean hasPermission(Object permission) {
try {
return super.hasPermission(null, null, permission);
} catch (AccessDeniedException e) {
return false;
}
}
public boolean checkPermission(Object permission) {
return super.hasPermission(null, null, permission);
}
@Override
public boolean hasPermission(Object targetId, String targetType, Object permission) {
try {
return super.hasPermission(targetId, targetType, permission);
} catch (AccessDeniedException e) {
return false;
}
}
public boolean checkPermission(Object targetId, String targetType, Object permission) {
return super.hasPermission(targetId, targetType, permission);
}
@Override
public boolean hasPermission(Object target, Object permission) {
try {
return super.hasPermission(target, permission);
} catch (AccessDeniedException e) {
return false;
}
}
public boolean checkPermission(Object target, Object permission) {
return super.hasPermission(target, permission);
}
}
As seen above I have added the new method testDecision(String), which I can successfully use in my preAuthorize call as below:
@PreAuthorize("testDecision('TestString')")
Event getEvent(int eventId);
But when I call it in the context of a PostAuthorize as:
@PostAuthorize("testDecision('TestString')")
Event getEvent(int eventId);
I get a ClassCastException:
SEVERE: Servlet.service() for servlet [Spring MVC Dispatcher Servlet] in context with path [/myapp] threw exception [Request processing failed; nested exception is java.lang.ClassCastException: com.example.CustomSecurityExpressionRoot cannot be cast to org.springframework.security.access.expression.method.MethodSecurityExpressionRoot] with root cause
java.lang.ClassCastException: com.example.CustomSecurityExpressionRoot cannot be cast to org.springframework.security.access.expression.method.MethodSecurityExpressionRoot
at org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler.setReturnObject(DefaultMethodSecurityExpressionHandler.java:156)
at org.springframework.security.access.expression.method.ExpressionBasedPostInvocationAdvice.after(ExpressionBasedPostInvocationAdvice.java:49)
at org.springframework.security.access.prepost.PostInvocationAdviceProvider.decide(PostInvocationAdviceProvider.java:38)
at org.springframework.security.access.intercept.AfterInvocationProviderManager.decide(AfterInvocationProviderManager.java:73)
at org.springframework.security.access.intercept.AbstractSecurityInterceptor.afterInvocation(AbstractSecurityInterceptor.java:282)
at org.springframework.security.access.intercept.aopalliance.MethodSecurityInterceptor.invoke(MethodSecurityInterceptor.java:68)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at com.sun.proxy.$Proxy15.getEvent(Unknown Source)
(..truncated..)
Anyone can help me figure out what I am doing wrong?
The class
CustomSecurityExpressionRoot
must extendsMethodSecurityExpressionRoot
! (implementingMethodSecurityExpressionOperations
) is not enough.Unfortunately
MethodSecurityExpressionRoot
is a package protected class.CustomSecurityExpressionRoot
in the same package (org.springframework.security.access.expression.method
)CustomSecurityExpressionRoot
(that is what I do in my projects)ExtensibleMethodSecurityExpressionRoot:
My complete way is this: ExtensibleMethodSecurityExpressionHandler to change the evaluation root context:
MethodSecurityExpressionRootFactory:
DefaultMethodSecuritiyExpressionRootFactory: only needed if one want to use the ExtensibleMethodSecurityExpression handler without own extension
Example Customized Method Expression Root
It seems you are on an older version of Spring Security. As of Spring Security 3.1.5+ SEC-2245 is fixed & you can create your own expression root and implement MethodSecurityExpressionOperations.