Closed
Description
Overview
- In the Java DSL configuration, the role prefix is correctly applied based on the configured
GrantedAuthorityDefaults
bean. - However, in the Kotlin DSL configuration, the role prefix is disregarded, leading to unexpected behavior.
Expected Behavior
- The role prefix configured in the GrantedAuthorityDefaults bean should be respected and applied consistently across both Java DSL and Kotlin DSL configurations.
Current Behavior
- The role prefix is properly applied in the Java DSL configuration but ignored in the Kotlin DSL configuration.
Example Code
1. Common - Controller
@RestController
class SecurityController {
@GetMapping("/")
fun index(): String {
return "index"
}
@GetMapping("/user")
fun user(): String {
return "user"
}
@GetMapping("/db")
fun db(): String {
return "db"
}
@GetMapping("/admin")
fun admin(): String {
return "admin"
}
}
2. Common - SecurityConfig
@Bean
fun grantedAuthorityDefaults(): GrantedAuthorityDefaults {
return GrantedAuthorityDefaults("MYPREFIX_")
}
@Bean
fun userDetailsService(): UserDetailsService {
val user = User.withUsername("user").password("{noop}1111").authorities("MYPREFIX_USER").build()
val db = User.withUsername("db").password("{noop}1111").authorities("MYPREFIX_DB").build()
val admin = User.withUsername("admin").password("{noop}1111").authorities("MYPREFIX_ADMIN").build()
return InMemoryUserDetailsManager(user, db, admin)
}
3. JavaDSL SecurityFilterChain
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http
.authorizeHttpRequests {
it
.requestMatchers("/user").hasRole("USER")
.requestMatchers("/admin").hasRole("ADMIN")
.requestMatchers("/db").hasRole("DB")
.anyRequest().authenticated()
}
.formLogin(Customizer.withDefaults())
return http.build()
}
4. KotlinDSL SecurityFilterChain
@Bean
fun securityFilterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeHttpRequests {
authorize("/user", hasRole("USER"))
authorize("/admin", hasRole("ADMIN"))
authorize("/db", hasRole("DB"))
authorize(anyRequest, authenticated)
}
formLogin { }
}
return http.build()
}
Probable Cause
- In Java DSL, the AuthorizeHttpRequestsConfigurer operates, utilizing the GrantedAuthorityDefaults bean registered by the developer to apply the role prefix.
// AuthorizeHttpRequestsConfigurer
public AuthorizeHttpRequestsConfigurer(ApplicationContext context) {
this.registry = new AuthorizationManagerRequestMatcherRegistry(context);
if (context.getBeanNamesForType(AuthorizationEventPublisher.class).length > 0) {
this.publisher = context.getBean(AuthorizationEventPublisher.class);
}
else {
this.publisher = new SpringAuthorizationEventPublisher(context);
}
this.roleHierarchy = SingletonSupplier.of(() -> (context.getBeanNamesForType(RoleHierarchy.class).length > 0)
? context.getBean(RoleHierarchy.class) : new NullRoleHierarchy());
String[] grantedAuthorityDefaultsBeanNames = context.getBeanNamesForType(GrantedAuthorityDefaults.class);
if (grantedAuthorityDefaultsBeanNames.length > 0) {
GrantedAuthorityDefaults grantedAuthorityDefaults = context.getBean(GrantedAuthorityDefaults.class);
this.rolePrefix = grantedAuthorityDefaults.getRolePrefix();
}
}
- However, in Kotlin DSL, the hasRole configuration invokes the hasRole method of AuthorizeHttpRequestsDsl, which in turn calls AuthorityAuthorizationManager.hasRole(role), disregarding the GrantedAuthorityDefaults bean registered by the developer.
// AuthorizeHttpRequestsDsl.hasRole
fun hasRole(role: String): AuthorizationManager<RequestAuthorizationContext> {
return AuthorityAuthorizationManager.hasRole(role)
}
// AuthorityAuthorizationManager
public static <T> AuthorityAuthorizationManager<T> hasRole(String role) {
Assert.notNull(role, "role cannot be null");
Assert.isTrue(!role.startsWith(ROLE_PREFIX), () -> role + " should not start with " + ROLE_PREFIX + " since "
+ ROLE_PREFIX + " is automatically prepended when using hasRole. Consider using hasAuthority instead.");
return hasAuthority(ROLE_PREFIX + role);
}
- This results in only the ROLE_ prefix being used, diverging from the developer's intention.
- For instance, a user with the "MYPREFIX_USER" role might fail authorization for the
/user
endpoint.