How do I remove the ROLE_ prefix from Spring Security with JavaConfig?
Solution 1
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>
Solution 2
The following configuration works for me.
@Override
public void configure(WebSecurity web) throws Exception {
web.expressionHandler(new DefaultWebSecurityExpressionHandler() {
@Override
protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, FilterInvocation fi) {
WebSecurityExpressionRoot root = (WebSecurityExpressionRoot) super.createSecurityExpressionRoot(authentication, fi);
root.setDefaultRolePrefix(""); //remove the prefix ROLE_
return root;
}
});
}
Solution 3
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;
}
}
Solution 4
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);
}
Solution 5
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>
Related videos on Youtube
Matt Raible
Web developer and Java Champion that loves to architect and build slick-looking UIs using CSS and JavaScript. When he's not evangelizing Okta and open source, he likes to ski with his family, drive his VWs, and enjoy craft beer.
Updated on September 29, 2020Comments
-
Matt Raible over 3 years
I'm trying to remove the "ROLE_" prefix in Spring Security. The first thing I tried was:
http.servletApi().rolePrefix("");
That didn't work, so I tried creating a
BeanPostProcessor
as suggested in http://docs.spring.io/spring-security/site/migrate/current/3-to-4/html5/migrate-3-to-4-jc.html#m3to4-role-prefixing-disable. That didn't work either.Finally, I tried creating my own
SecurityExpressionHandler
:@Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .expressionHandler(webExpressionHandler()) .antMatchers("/restricted").fullyAuthenticated() .antMatchers("/foo").hasRole("mycustomrolename") .antMatchers("/**").permitAll(); } private SecurityExpressionHandler<FilterInvocation> webExpressionHandler() { DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler = new DefaultWebSecurityExpressionHandler(); defaultWebSecurityExpressionHandler.setDefaultRolePrefix(""); return defaultWebSecurityExpressionHandler; }
However, this doesn't work either. If I use "hasAuthority(roleName)" instead of
hasRole
, it works as expected.Is it possible to remove the ROLE_ prefix from Spring Security's hasRole check?
-
M. Deinum almost 8 yearsStrange the
BeanPostProcessor
works for me (you did declare it as astatic
bean method and included thePriorityOrdered
so that it runs very early?) and the same for expression handler. We also have aDefaultMethodSecurityExpressionHandler
DefaultMethodSecurityExpressionHandler` configured with the prefix set thenull
. -
Matt Raible almost 8 yearsYes, I copied the code for the
BeanPostProcessor
directly from the documentation. I tried putting the@Bean
in my@Configuration
class for Spring Security and in my@SpringBootApplication
class. I added aSystem.out.println
to ensure it's being configured before Spring Security too.hasAuthority
works as expected, so I guess I'll just use that instead. -
M. Deinum almost 8 yearsWe have it in a non spring boot application. Could it be that that is interfering or that the security of boot is somehow still configured earlier?
-
-
Mostafa Barmshory almost 7 yearsIs there any equivalent in XML configuration?
-
Piotr Tempes almost 7 yearsI have updated my answer with xml config... but as I wrote, there might be some kind of a mistake - I did not tested it out. The general rule is - everything in java config can be done in xml as well.
-
Brenno Leal over 6 yearsDidnt Worked for me
-
Petr Kozelka over 6 yearsfails in my JHipster application right during boot, with
org.springframework.beans.factory.BeanCreationException: .......; nested exception is java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
-
Piotr Tempes over 6 yearsPetr - it's just a wild guess, but if you get this error it probably means you do not have a web context so you do not need the second bean - DefaultWebSecurityExpressionHandler. Anyway each configuration is different, so it is hard to tell what the problem really is in your case guys. For me these two beans fixed the problem.
-
Gaetan about 6 yearsDoes not work: see the 4th comment in question stackoverflow.com/questions/46756013
-
Charles Morin almost 6 yearsThere is a difference between a GrantedAuthority and a Role. This answer is for GrantedAuthority.
-
Ninja over 5 yearsI use the @EnableGlobalMethodSecurity in WebSecurityConfigurerAdapter, how to get access to AccessDecisionManager?
-
Jeff Sheets over 5 years@Ninja The AccessDecisionManager will have to be configured in an additional class, as I don't believe it can be done from the WebSecurityConfigurerAdapter. I think you'll just need to create a new Configuration class that extends GlobalMethodSecurityConfiguration
-
Ismail Yavuz about 5 yearsFor those who have exception with annotation: move the @Bean instance from the security config to the MVC config Ref: stackoverflow.com/questions/48971937/…
-
Yougesh almost 5 yearsBut that only helps if you use
@Secured
annotation because@Secured
values are evaluated inRoleVoter
class, and@PreAuthorize
annotation expression evaluated inPreInvocationAuthorizationAdviceVoter
class, publishing another beannew GrantedAuthorityDefaults("")
with prefix argument in constructor which also affect on@PreAuthorize
. -
mojtab23 over 4 yearsIt works with
HttpServletRequest.isUserInRole(String)
but not works with@Secured(String)
.