В двух словах
Предоставленные полномочия не демонстрируют префикс ROLE_
и поэтому игнорируются RoleVoter
.
Замена RoleVoter
с пользовательской реализацией, которая имеет пустой префикс, решает проблему.
Полная история
Теперь остается вопрос, почему мы вообще можем получить доступ к представлению.Объяснение простое, но требует немного большего контекста.
Мы работаем над переносом приложения Thorntail на Spring Boot.
И мы используем Vaadin 8 (потому что у нас есть устаревший Vaadin 7вещи, от которых нам пока не удалось избавиться и которые необходимо поддерживать).
И так?
Vaadin - это одностраничный фреймворк, а в Vaadin 8 есть такой неприятный способ ссылки на представления.а именно, он использует #!
в корневом URL-адресе (например, https://<host>:<port>/#!foo/bar/baz/...
).
Ничего после того, как #
не отправлено на сервер, это означает, что мы не можем различить доступ к /
идоступ к /#!foo/bar/baz/...
Таким образом, мы не можем использовать Spring Security для защиты доступа к представлениям.
И у нас есть представление Vaadin, к которому нам нужно разрешить доступ без аутентификации.
В результате мы были вынуждены разрешить неаутентифицированный доступ к /
.MainUI
(который обрабатывает все входящие запросы) проверяет, прошел ли пользователь аутентификацию, и перенаправляет на страницу входа, если это не так.
Доступ к самим представлениям защищен Vaadin, а не Spring.SpringViewProvider
не найдет View
, к которому пользователь не должен иметь доступ (как следствие, пользователь не сможет там перемещаться).
Однако, как только мы вызовем метод в этом представлении, Spring Spring активирует и выполняет проверки доступа. эти проверки завершились неудачно, поскольку предоставленные полномочия не имели префикса "ROLE _", ожидаемого Spring.(Я предполагал, что это просто соглашение, но это не так; RoleVoter
фактически игнорирует полномочия, которые не показывают префикс, поэтому вы ДОЛЖНЫ сделать это таким образом, ИЛИ вы должны предоставить свой RoleVoter
.)
Решение было относительно простым ...
@Configuration
@EnableGlobalMethodSecurity(
securedEnabled = true,
prePostEnabled = true,
proxyTargetClass = true
)
class GlobalSecurityConfiguration : GlobalMethodSecurityConfiguration(){
override fun accessDecisionManager(): AccessDecisionManager {
return (super.accessDecisionManager() as AffirmativeBased).apply {
decisionVoters.replaceAll {
when(it){
is RoleVoter -> MyRoleVoter()
else -> it
}
}
}
}
}
, где
class MyRoleVoter : RoleVoter(){
init {
rolePrefix = ""
}
}