Нужно ли размещать мой пользовательский фильтр перед конкретным встроенным фильтром, чтобы гарантировать выполнение шага авторизации после выполнения этого фильтра?
Ваш фильтр ДОЛЖЕН предшествовать 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");