I\'m trying to remove the \"ROLE_\" prefix in Spring Security. The first thing I tried was:
http.servletApi().rolePrefix(\"\");
That didn\'
I post summary working solutions for me:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
/**
* Allow skip ROLE_ when check permission using @Secured, like:
* @Secured({AuthorityConstants.ROLE_SYSTEM_ADMIN})
*/
@Override
protected AccessDecisionManager accessDecisionManager() {
AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager();
setAuthorityRolePrefix(accessDecisionManager, "");
return accessDecisionManager;
}
private void setAuthorityRolePrefix(AffirmativeBased accessDecisionManager, String rolePrefix) {
accessDecisionManager.getDecisionVoters().stream()
.filter(RoleVoter.class::isInstance)
.map(RoleVoter.class::cast)
.forEach(it -> it.setRolePrefix(rolePrefix));
}
/**
* Allow skip ROLE_ when check permission using @PreAuthorize, like:
* @PreAuthorize("hasAnyRole('USER', 'SYSTEM_ADMIN')")
*/
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
}
}
With Spring Boot 2.3 I got this exception at boot time:
Error creating bean with name 'resourceHandlerMapping' defined in class path resource
[org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]:
Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException:
Failed to instantiate [org.springframework.web.servlet.HandlerMapping]:
Factory method 'resourceHandlerMapping' threw exception;
nested exception is java.lang.IllegalStateException: No ServletContext set
Here is my solution:
@Configuration
@Import(RolePrefixConfiguration.class)
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class MyWebSecurityConfiguration extends WebSecurityConfigurerAdapter {
public static class RolePrefixConfiguration {
@Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
log.debug("remove prefix 'ROLE_' from grantedAuthorityDefaults");
return new GrantedAuthorityDefaults("");
}
}
// ... your usual config
}
It appears the new GrantedAuthorityDefaults
will change the prefix for the DefaultWebSecurityExpressionHandler
and the DefaultMethodSecurityExpressionHandler
, but it doesn't modify the RoleVoter.rolePrefix
that is setup from @EnableGlobalMethodSecurity
.
The RoleVoter.rolePrefix is what is used for @Secured("ADMIN")
style of method security.
So along with the GrantedAuthorityDefaults
, I had to also add this CustomGlobalMethodSecurity
class to override the defaults for RoleVoter
.
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class CustomGlobalMethodSecurity extends GlobalMethodSecurityConfiguration {
protected AccessDecisionManager accessDecisionManager() {
AffirmativeBased accessDecisionManager = (AffirmativeBased) super.accessDecisionManager();
//Remove the ROLE_ prefix from RoleVoter for @Secured and hasRole checks on methods
accessDecisionManager.getDecisionVoters().stream()
.filter(RoleVoter.class::isInstance)
.map(RoleVoter.class::cast)
.forEach(it -> it.setRolePrefix(""));
return accessDecisionManager;
}
}
Starting from Spring 4.2, you can define the prefix with a single bean, as described here: https://github.com/spring-projects/spring-security/issues/4134
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
}
XML version:
<beans:bean id="grantedAuthorityDefaults" class="org.springframework.security.config.core.GrantedAuthorityDefaults">
<beans:constructor-arg value="" />
</beans:bean>
If you use Spring Boot 2, you can create this bean to override the RoteVoter prefix
@Bean
public GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults("<anything you want>");
}
It works because when GlobalMethodSecurityConfiguration creates AccessDecisionManager in the method GlobalMethodSecurityConfiguration.accessDecisionManager(). Here is the snippet of code, notice the null check on grantedAuthorityDefaults
protected AccessDecisionManager accessDecisionManager() {
....
RoleVoter roleVoter = new RoleVoter();
GrantedAuthorityDefaults grantedAuthorityDefaults =
getSingleBeanOrNull(GrantedAuthorityDefaults.class);
if (grantedAuthorityDefaults != null) {
roleVoter.setRolePrefix(grantedAuthorityDefaults.getRolePrefix());
}
decisionVoters.add(roleVoter);
decisionVoters.add(new AuthenticatedVoter());
return new AffirmativeBased(decisionVoters);
}
If you are prior to 4.2 and are using so called voters (you are if you use annotations like @hasRole etc) then you need to define below beans in the context:
@Bean
public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler = new DefaultMethodSecurityExpressionHandler();
defaultMethodSecurityExpressionHandler.setDefaultRolePrefix("");
return defaultMethodSecurityExpressionHandler;
}
@Bean
public DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler() {
DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler();
defaultWebSecurityExpressionHandler.setDefaultRolePrefix("");
return defaultWebSecurityExpressionHandler;
}
These beans are used to create evaluation context for spell expressions and they have a defaultRolePrefix set to 'ROLE_'. Although it depends on your use case. This one worked for me and above didn't.
EDIT: answering question about xml configuration -> of course it can be done in xml. Everything done in java config can be written in xml configuration. Here is example (although I did not test it so there might be a typo or something):
<bean id="defaultWebSecurityExpressionHandler" class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler">
<property name="defaultRolePrefix" value=""></property>
</bean>
<bean id="defaultMethodSecurityExpressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
<property name="defaultRolePrefix" value=""></property>
</bean>