Spring Security Custom Authentication Фильтр и авторизация - PullRequest
0 голосов
/ 03 июля 2018

Я реализовал пользовательский фильтр аутентификации, и он прекрасно работает. Я использую внешний поставщик удостоверений и перенаправляю на первоначально запрошенный URL-адрес после настройки сеанса и добавления объекта аутентификации в контекст безопасности.

Настройка безопасности

@EnableWebSecurity(debug = true)
@Configuration
class SecurityConfig extends WebSecurityConfigurerAdapter {

    // this is needed to pass the authentication manager into our custom security filter
    @Bean
    @Override
    AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean()
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                //.antMatchers("/admin/test").hasRole("METADATA_CURATORZ")
                .antMatchers("/**").permitAll()
            .anyRequest().authenticated()
            .and()
            .addFilterBefore(new CustomSecurityFilter(authenticationManagerBean()), UsernamePasswordAuthenticationFilter.class)
    }
}

Логика фильтра

Пока мой пользовательский фильтр (после подтверждения личности) просто жестко кодирует роль:

SimpleGrantedAuthority myrole = new SimpleGrantedAuthority("METADATA_CURATORZ")
                return new PreAuthenticatedAuthenticationToken(securityUser, null, [myrole])

Этот объект аутентификации (возвращается выше) затем добавляется в мой SecurityContext перед перенаправлением на желаемую конечную точку:

SecurityContextHolder.getContext().setAuthentication(authentication)

Конечная точка контроллера

  @RequestMapping(path = '/admin/test', method = GET, produces = 'text/plain')
  String test(HttpServletRequest request) {
    Authentication auth = SecurityContextHolder.getContext().getAuthentication()

    String roles = auth.getAuthorities()
    return "roles: ${roles}"
  }

Затем эта конечная точка выдает в браузере ответ:

"Роли: [METADATA_CURATORZ]"

Отлично. Так что моя аутентификация и применение роли к моему пользователю работает отлично.

Теперь, если я раскомментирую эту строку из конфигурации безопасности:

//.antMatchers("/admin/test").hasRole("METADATA_CURATORZ")

Я больше не могу получить доступ к этому ресурсу и получить 403 - хотя мы уже доказали, что роль установлена.

Мне это кажется совершенно бессмысленным и сломанным, но я не эксперт по безопасности в Spring.

Я, наверное, упускаю что-то очень простое. Есть идеи?

У меня есть несколько вопросов:

  • Нужно ли размещать мой пользовательский фильтр перед определенным встроенным фильтром, чтобы гарантировать выполнение шага авторизации после выполнения этого фильтра?
  • Когда в цикле запроса выполняется проверка antMatcher / hasRole?
  • Нужно ли мне менять порядок звонков в моей цепочке настройки безопасности и как понимать конфигурацию в том виде, в котором я ее сейчас написал? Очевидно, он не делает то, что, как я думаю, должно быть.

1 Ответ

0 голосов
/ 11 февраля 2019

Нужно ли размещать мой пользовательский фильтр перед конкретным встроенным фильтром, чтобы гарантировать выполнение шага авторизации после выполнения этого фильтра?

Ваш фильтр ДОЛЖЕН предшествовать FilterSecurityInterceptor, потому что именно здесь происходит авторизация и аутентификация. Этот фильтр является одним из последних, которые будут вызваны.

Теперь о том, где может быть лучшее место для вашего фильтра, это действительно зависит. Например, вы действительно хотите, чтобы ваш фильтр был до AnonymousAuthenticationFilter, потому что в противном случае неаутентифицированные пользователи всегда будут «аутентифицироваться» с AnonymousAuthenticationToken к тому времени, когда ваш фильтр будет активирован.

Вы можете проверить порядок фильтров по умолчанию в FilterComparator. AbstractPreAuthenticatedProcessingFilter в значительной степени соответствует тому, что вы делаете, и его расположение в порядке фильтров дает вам представление о том, где вы можете разместить свои. В любом случае не должно быть проблем с заказом вашего фильтра.

Когда в цикле запроса выполняется проверка antMatcher / hasRole?

Все это происходит в FilterSecurityInterceptor, а точнее, в его родительском AbstractSecurityInterceptor:

protected InterceptorStatusToken beforeInvocation(Object object) {

    Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
            .getAttributes(object);

    if (attributes == null || attributes.isEmpty()) {
        ...
    }

    ...

    Authentication authenticated = authenticateIfRequired();

    // Attempt authorization
    try {
        this.accessDecisionManager.decide(authenticated, object, attributes);
    }
    catch (AccessDeniedException accessDeniedException) {

        ...

        throw accessDeniedException;
    }

Дополнительная информация: По сути, FilterSecurityInterceptor имеет ExpressionBasedFilterInvocationSecurityMetadataSource, который содержит Map<RequestMatcher, Collection<ConfigAttribute>>. Во время выполнения ваш запрос проверяется на соответствие Map, чтобы определить, совпадает ли ключ RequestMatcher. Если это так, Collection<ConfigAttribute> передается в AccessDecisionManager, который в конечном итоге либо предоставляет, либо запрещает доступ. Значение по умолчанию AccessDecisionManager равно AffirmativeBased и содержит объекты (обычно WebExpressionVoter), которые обрабатывают коллекцию ConfigAttribute и с помощью отражения вызывают SpelExpression, который соответствует вашему "hasRole('METADATA_CURATORZ')", против объекта SecurityExpressionRoot, который был инициализирован с вашим Authentication.

Нужно ли мне менять порядок того, что я вызываю в моей цепочке настройки безопасности, и как мне понимать конфигурацию в том виде, в котором я ее сейчас написал? Очевидно, он не делает то, что, как я думаю, должно быть.

Нет, с вашими фильтрами не должно быть проблем. Как примечание, в дополнение к тому, что вы используете в своих configure(HttpSecurity http) методах, WebSecurityConfigurerAdapter, из которого вы расширяете, имеет некоторые значения по умолчанию:

http
    .csrf().and()
    .addFilter(new WebAsyncManagerIntegrationFilter())
    .exceptionHandling().and()
    .headers().and()
    .sessionManagement().and()
    .securityContext().and()
    .requestCache().and()
    .anonymous().and()
    .servletApi().and()
    .apply(new DefaultLoginPageConfigurer<>()).and()
    .logout();

Вы можете взглянуть на HttpSecurity, если хотите точно узнать, что они делают и какие фильтры они добавляют.

ПРОБЛЕМА

Когда вы делаете следующее:

.authorizeRequests()
    .antMatchers("/admin/test").hasRole("METADATA_CURATORZ")

... ищется роль "ROLE_METADATA_CURATORZ". Зачем? ExpressionUrlAuthorizationConfigurer метод static hasRole(String role) завершает обработку "METADATA_CURATORZ":

if (role.startsWith("ROLE_")) {
    throw new IllegalArgumentException(
                "role should not start with 'ROLE_' since it is automatically inserted. Got '"
                        + role + "'");
    }
    return "hasRole('ROLE_" + role + "')";
}

Таким образом, ваше выражение авторизации становится "hasRole('ROLE_METADATA_CURATORZ'", и в результате вызывается метод hasRole('ROLE_METADATA_CURATORZ') для SecurityExpressionRoot, который, в свою очередь, ищет роль ROLE_METADATA_CURATORZ в полномочиях Authentication.

РЕШЕНИЕ

Изменение

SimpleGrantedAuthority myrole = new SimpleGrantedAuthority("METADATA_CURATORZ");

до:

SimpleGrantedAuthority myrole = new SimpleGrantedAuthority("ROLE_METADATA_CURATORZ");
...