Clark Duplichien (Migrated from SEC-2026) said:
Similar to SEC-1691, except don't need to specify as part of config... just need it to be public. Currently, you can extend DefaultMethodSecurityExpressionHandler and provide the reference to the expression-handler element of the global-method-security config, but even if you override the createSecurityExpressionRoot method, you can't provide an instance that extends MethodSecurityExpressionRoot, because it's protected. This is also inconsistent with MethodSecurityExpressionRoot, which is already public.
Comment From: spring-projects-issues
Clark Duplichien said:
That last bit should read "inconsistent with WebSecurityExpressionRoot, which is already public."
Comment From: spring-projects-issues
Clark Duplichien said:
Oh, and DefaultMethodSecurityExpressionHandler.setReturnObject needs to use the MethodSecurityExpressionOperations interface rather than casting to MethodSecurityExpressionRoot.
Comment From: spring-projects-issues
Darren Gorman said:
Any update on this? Class is still hidden as of 3.1.3.RELEASE.
Comment From: OrangeDog
It really should be easier to add to the expression context than having to completely re-implement this class.
Comment From: rwinch
@OrangeDog What exactly do you want to expand? Have you tried using a Bean. For example, you can do something like:
@beanName.methodThatReturnsBoolean(some args...)
Comment From: OrangeDog
It's a bit much just to add a couple more string constants to the existing read, write, etc.
Or extend hasRole to support multiple prefixes.
Or any other number of simple additions without requiring verbose syntax.
Comment From: rwinch
I'm not sure why it is a bit much. You can accomplish this goal by creating a single Bean. One option is to do something like:
@Component
public class Authz {
public boolean hasReadAccess(MyUser myUser, Object object) {
// ...
return result;
}
public boolean hasWriteAccess(MyUser myUser, Object object) {
// ...
return result;
}
}
Then you can leverage the bean using:
@PostAuthorize("@authz.hasReadAccess(principal, returnObject)")
This has the additional benefit of not being coupled to the Spring Security APIs since the Bean you have defined is relying only on your domain objects (rather than having Spring Security's Authentication object in the method signature).
Alternatively, you can create a bean with the values on it and refer to it that way. For example:
@Component
public class Permissions
public final String read = "read";
public final String write = "write";
}
Then you can leverage it using:
@PostAuthorize("hasPermission(returnObject, @permissions.read")
If you are wanting to create a custom MethodSecurityExpressionRoot, you would need to create at least one Bean.
I'm not convinced there is a solid reason for making MethodSecurityExpressionRoot public. Code we expose makes additional changes much more challenging. For example, proper scoping recently allowed us make changes to support path variables within the web layer very easily because we did not have to worry about passivity.
Comment From: simplicii
Vote for Pull Request to change this: SEC-2026: MethodSecurityExpressionRoot should be a public class #4266
Comment From: marcusdacoregio
Thanks for the discussion everyone!
I'm closing this since we are not looking to change MethodSecurityExpressionRoot to a public class.
There are some ways to achieve the goal as described in the comments in this issue.
Comment From: ch4mpy
java @PostAuthorize("@authz.hasReadAccess(principal, returnObject)")
Is still more than
@PostAuthorize("hasReadAccess(returnObject)")
And it does sensible difference when expression grows:
@PreAuthorize("isNice() or onBehalfOf(#otherSubject).can('greet')")
// is certainly more readable than
@PreAuthorize("@myExpressionHandler.isNice(principal) or @myExpressionHandler.onBehalfOf(principal, #otherSubject).can('greet')")
With slightly modified copies of MethodSecurityExpressionRoot and MethodSecurityExpressionHandler, I can use the short version of the expression above with just this config:
@Bean
public MethodSecurityExpressionHandler methodSecurityExpressionHandler() {
return new C4MethodSecurityExpressionHandler(MyMethodSecurityExpressionRoot::new);
}
static final class MyMethodSecurityExpressionRoot extends C4MethodSecurityExpressionRoot {
public Proxy onBehalfOf(String proxiedUserSubject) {
return getAuth().getProxyFor(proxiedUserSubject);
}
public boolean isNice() {
return hasAnyAuthority("ROLE_NICE_GUY", "SUPER_COOL");
}
}
Sad that I had to copy / paste protected code for that :/