Unfortunately there is no support to add role hierarchy to JSR250 manager. But there is a workaround that basically clones the library's implementation. At this point it makes more sense to drop JSR250 since you will be just replicating the logic from the libs to your code base, but if you want to do it anyway, just follow these instructions:
- Create a custom manager that will deal with the JSR250 annotations:
public class Jsr250CustomAuthorizationManager implements AuthorizationManager<MethodInvocation> {
static final AuthorizationManager<MethodInvocation> NULL_MANAGER = (a, o) -> null;
private final RoleHierarchy roleHierarchy;
public Jsr250CustomAuthorizationManager(RoleHierarchy roleHierarchy) {
this.roleHierarchy = roleHierarchy;
}
@Override
public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation methodInvocation) {
var manager = resolveManager(methodInvocation);
return manager.check(authentication, methodInvocation);
}
public AuthorizationManager<MethodInvocation> resolveManager(MethodInvocation methodInvocation) {
if (hasDenyAll(methodInvocation)) {
return (a, o) -> new AuthorizationDecision(false);
}
if (hasPermitAll(methodInvocation)) {
return (a, o) -> new AuthorizationDecision(true);
}
if (hasRolesAllowed(methodInvocation)) {
var rolesAllowed = (RolesAllowed) methodInvocation.getMethod().getAnnotation(RolesAllowed.class);
var manager = AuthorityAuthorizationManager.<MethodInvocation>hasAnyRole(rolesAllowed.value());
manager.setRoleHierarchy(roleHierarchy);
return manager;
}
return NULL_MANAGER;
}
public boolean hasDenyAll(MethodInvocation methodInvocation) {
return methodInvocation.getMethod().getAnnotation(DenyAll.class) != null;
}
public boolean hasPermitAll(MethodInvocation methodInvocation) {
return methodInvocation.getMethod().getAnnotation(PermitAll.class) != null;
}
public boolean hasRolesAllowed(MethodInvocation methodInvocation) {
return methodInvocation.getMethod().getAnnotation(RolesAllowed.class) != null;
}
}
You can just copy and paste the code above into a new class in your code, this is a class that I made myself based on the one that exists in the spring security lib.
- expose this new instance as a bean of type
Advisor
:
@EnableMethodSecurity(jsr250Enabled = true)
public class MethodSecuritytConfiguration {
@Bean
public Advisor jsr250AuthorizationMethodInterceptor() {
RoleHierarchyImpl roleHierarchy = new RoleHierarchyImpl();
roleHierarchy.setHierarchy("ROLE_ADMIN > ROLE_USER");
var manager = new Jsr250CustomAuthorizationManager(roleHierarchy);
return AuthorizationManagerBeforeMethodInterceptor.jsr250(manager);
}
}
- In case you are using
@EnableMethodSecurity(jsr250Enabled = true)
, you will also need to add this configuration to your application.properties
:
spring.main.allow-bean-definition-overriding=true
This will allow to override a bean that is defined in the security lib that deals with JSR250 annotations. Because this bean's configuration is hardcoded in the lib wihtout exposing a way to change it, we have to override it altogheter to add the behavior we need. Note that the name of your bean needs to be jsr250AuthorizationMethodInterceptor
to override the one (with same name) from the security lib. If you remove the jsr250Enabled
configuration from EnableMethodSecurity
, then you can name your bean anything you want and remove the configuration from application.properties
.