Summary
Since 4.2, we can control the default role prefix(default is ROLE_) at the single point of definition (gh-3701).
However, it not apply to access control definitions using the ExpressionInterceptUrlRegistry#hasRole().
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
GrantedAuthorityDefaults grantedAuthorityDefaults() {
return new GrantedAuthorityDefaults(""); // Remove the ROLE_ prefix
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// ...
http.authorizeRequests()
.anyRequest().hasRole("USER"); // Allow access from user granted USER role
}
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
userDetailsManager.createUser(
User.withUsername("kazuki43zoo")
.password("password")
.authorities("USER") // Grant the USER role (without ROLE_ prefix)
.build());
return userDetailsManager;
}
}
Actual Behavior
If login with the kazuki43zoo, 403 Forbidden error has been occurred.
Whitelabel Error Page
This application has no explicit mapping for /error, so you are seeing this as a fallback.
Wed Nov 16 01:06:38 JST 2016
There was an unexpected error (type=Forbidden, status=403).
Access is denied
Expected Behavior
Can access the specified resource after authentication success. (200 OK)
Version
4.2.0
Workaround
It work as following configuration instead of.
@Override
protected void configure(HttpSecurity http) throws Exception {
// ...
http.authorizeRequests()
.anyRequest().access("hasRole('USER')"); // Use the access() instead of hasRole()
}
Comment From: kazuki43zoo
Related with this, the UserBuilder#roles method append the ROLE_ prefix to granted authority. Is this behavior works as designed ?
@Bean
public UserDetailsService userDetailsService() {
InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager();
userDetailsManager.createUser(
User.withUsername("kazuki43zoo")
.password("password")
.roles("USER") // has been append the ROLE_ prefix
// .authorities("USER")
.build());
return userDetailsManager;
}
In this case, we can use the authorities method instead of.
Comment From: tomas-silhavy
I can confirm that it's not working in version 4.2.3. The problem lies here
ExpressionUrlAuthorizationConfigurer.hasRole(role)
vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
private static String hasRole(String role) {
Assert.notNull(role, "role cannot be null");
if(role.startsWith("ROLE_")) {
throw new IllegalArgumentException("role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'");
} else {
return "hasRole('ROLE_" + role + "')";
}
}
and same constant string "ROLE_" is on several places in this class.
Comment From: jeffsheets
Possibly related to this: https://stackoverflow.com/a/46817507/1469525 Or maybe completely separate.
GrantedAuthorityDefaults will change the prefix for the DefaultWebSecurityExpressionHandler and the DefaultMethodSecurityExpressionHandler, but it doesn't modify the RoleVoter.rolePrefix that is setup from @EnableGlobalMethodSecurity.
It would be nice if the RoleVoter also could get the rolePrefix from GrantedAuthorityDefaults
Comment From: BenDol
protected AccessDecisionManager accessDecisionManager() { inside GlobalMethodSecurityConfiguration this is simply calling new RoleVoter() rather than using the Bean factory that has the proper prefix configuration.