Spring security jdbcAuthentication не работает с обработкой ролей по умолчанию

С использованием

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
         auth.inMemoryAuthentication().withUser("dba").password("root123").roles("ADMIN","DBA");

мой пример работает нормально. Например для

      http.authorizeRequests()
        // ...
        .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
        .and().formLogin()
        .and().exceptionHandling().accessDeniedPage("/Access_Denied");

Если я изменил inMemoryAuthentication на spring jdbc по умолчанию, у меня возникла проблема с ролью.

    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
         auth.jdbcAuthentication().dataSource(dataSource);

Я уверен, что настроил базу данных и схему, используя весенние рекомендации (чтобы иметь возможность использовать аутентификацию jdbc по умолчанию).

В режиме отладки я вижу результат загрузки из БД в

org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl
    #loadUserByUsername(username)[line 208]
    return createUserDetails(username, user, dbAuths);

Он возвращает аналогичный результат с конфигурацией памяти:

org.springframework.security.core.userdetails.User@183a3:
     Username: dba;
     Password: [PROTECTED];
     Enabled: true;
     AccountNonExpired: true;
     credentialsNonExpired: true;
     AccountNonLocked: true;
     Granted Authorities: ADMIN,DBA

Как видите, он загружает соответствующие предоставленные полномочия, но http-запрос перенаправляет меня на .accessDeniedPage("/Access_Denied"). Я запутался, потому что он должен работать для пользователя, как раньше.

Я не использую весеннюю загрузку в своем проекте. Мои журналы не содержат никакой конфигурации ошибок jdbc. Я потратил много времени на изучение деталей, и мои идеи только что закончились. Как вы думаете, мне нужно добавить, чтобы создать какие-то библиотеки кеша или что-то еще?


person Sergii    schedule 09.03.2016    source источник
comment
Нет, не должно... При использовании встроенной базы данных роли автоматически имеют префикс ROLE_ (префикс роли по умолчанию). То же самое касается hasRole('ADMIN'), который также проверит, имеет ли переданная роль префикс, если нет, он будет добавлен. У вашего пользователя есть полномочия ADMIN, а не ROLE_ADMIN, поэтому проверка не удалась. Либо используйте hasAuthority вместо hasRole (и измените roles на authorities в вашем образце в памяти), либо добавьте к полномочиям в базе данных префикс ROLE_, либо измените префикс роли по умолчанию на пустой вместо ROLE_.   -  person M. Deinum    schedule 09.03.2016
comment
Большое спасибо! Теперь это работает. Не могли бы вы добавить ссылку на сравнение ролей (надеюсь, эта информация будет мне полезна в ближайшем будущем)?   -  person Sergii    schedule 09.03.2016


Ответы (2)


Здесь есть 2 ловушки.

Во-первых, при использовании hasRole('ADMIN') сначала выполняется проверка, начинается ли он с префикса роли (для которого по умолчанию используется ROLE_), если переданная роль не является префиксом с ним (см. также справочное руководство). Таким образом, в этом случае фактическая проверка полномочий равна ROLE_ADMIN, а не ADMIN, как вы ожидаете/предполагаете.

Во-вторых, при использовании параметра в памяти метод roles делает то же самое, что и здесь. Он проверяет, начинаются ли переданные роли с префикса роли, и если нет, то добавляет его. Таким образом, в вашем образце с тем, который находится в памяти, вы получаете полномочия ROLE_ADMIN и ROLE_DBA.

Однако в вашем варианте JDBC у вас есть полномочия ADMIN и DBA, и, следовательно, проверка hasRole('ADMIN') завершается неудачей, поскольку ROLE_ADMIN не равно ADMIN.

Чтобы исправить у вас есть несколько вариантов.

  1. Вместо hasRole используйте hasAuthority, последний не добавляет префикс роли, а для опции в памяти используйте authorities вместо roles.
  2. В опции JDBC перед полномочиями в базе данных ставится префикс ROLE_.
  3. Задайте пустой префикс роли по умолчанию.

Использование hasAuthority

Сначала измените конфигурацию базы данных в памяти, чтобы использовать authorities вместо roles.

auth.inMemoryAuthentication()
    .withUser("dba").password("root123")
    .authorities("ADMIN","DBA");

затем измените свои выражения, а также

.antMatchers("/db/**").access("hasAuthority('ADMIN') and hasAuthority('DBA')")

Префикс с ROLE_

В сценарии, который вставляет полномочия, добавьте к ним префикс ROLE_.

Удалить префикс роли по умолчанию

Это немного сложно и подробно описано в [руководстве по миграции].

Не существует простого варианта конфигурации, и для него требуется файл BeanPostProcessor.

public class DefaultRolesPrefixPostProcessor implements BeanPostProcessor, PriorityOrdered {

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {

        // remove this if you are not using JSR-250
        if(bean instanceof Jsr250MethodSecurityMetadataSource) {
            ((Jsr250MethodSecurityMetadataSource) bean).setDefaultRolePrefix(null);
        }

        if(bean instanceof DefaultMethodSecurityExpressionHandler) {
            ((DefaultMethodSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
        }
        if(bean instanceof DefaultWebSecurityExpressionHandler) {
            ((DefaultWebSecurityExpressionHandler) bean).setDefaultRolePrefix(null);
        }
        if(bean instanceof SecurityContextHolderAwareRequestFilter) {
            ((SecurityContextHolderAwareRequestFilter)bean).setRolePrefix("");
        }
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        return bean;
    }

    @Override
    public int getOrder() {
        return PriorityOrdered.HIGHEST_PRECEDENCE;
    }
}
person M. Deinum    schedule 10.03.2016

Вы можете увидеть, что произошло, включив ведение журнала. В application.properties добавьте:

# ==============================================================
# = Logging springframework
# ==============================================================
logging.level.org.springframework.jdbc=DEBUG
logging.level.org.springframework.security=DEBUG
logging.level.org.springframework.web=DEBUG
logging.level.org.springframework.http=DEBUG
person Rafael S. Fijalkowski    schedule 28.12.2018