У меня есть приложение Spring Boot, использующее репозитории JPA для доступа к БД.По умолчанию Spring создает перехватчик методов со стандартным AccessDecisionManager, который содержит избирателей, которые будут определять, могут ли пользователи получать доступ к методам.
Стандартный AccessDecisionManager содержит несколько избирателей и выглядит так в GlobalMethodSecurityConfiguration.java:
protected AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<AccessDecisionVoter<? extends Object>>();
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(getExpressionHandler());
if (prePostEnabled()) {
decisionVoters
.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
}
if (jsr250Enabled()) {
decisionVoters.add(new Jsr250Voter());
}
decisionVoters.add(new RoleVoter());
decisionVoters.add(new AuthenticatedVoter());
return new AffirmativeBased(decisionVoters);
}
Для наших целей нам нужно было добавить пару пользовательских избирателей для обеспечения безопасности на уровне метода.Согласно документации, мы можем переопределить этот метод, чтобы сделать именно это.Итак, мы создали наш собственный класс с именем MethodSecurityConfig.java.Его сигнатура класса выглядит следующим образом:
@Slf4j
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration
Этот класс конфигурации содержит один метод, который переопределяет AccessDecisionManager уровня метода:
@Override
protected AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<AccessDecisionVoter<? extends Object>>();
ExpressionBasedPreInvocationAdvice expressionAdvice = new ExpressionBasedPreInvocationAdvice();
expressionAdvice.setExpressionHandler(getExpressionHandler());
decisionVoters.add(new PreInvocationAuthorizationAdviceVoter(expressionAdvice));
decisionVoters.add(new RoleVoter());
decisionVoters.add(new AuthenticatedVoter());
decisionVoters.add(new CustomMethodVoter());
return new AffirmativeBased(decisionVoters);
}
При запуске приложения мы видим, что все это выполняетсякрасиво.Вот где это становится интересным.
При локальном запуске приложения все работает отлично.Пользователи заблокированы от вызова методов, для которых они не авторизованы.Тем не менее, при запуске приложения в наших более высоких средах (dev, tst и т. Д.) Ни один из этих избирателей не вызывается.
Я включил удаленную отладку на нашем устройстве dev, и кажется, что я понял что не вызывается, но я не понимаю почему это не вызывается.При запуске приложения создается пользовательский AccessDecisionManager, и он даже вызывается несколько раз.
Однако он не всегда вызывается.Главный пример - сохранение нового объекта.Все наши объекты проходят один и тот же процесс проверки (при необходимости), а затем сохраняются.Код выглядит следующим образом:
protected T validateAndSave(T entity) {
validator.validateBeforeAnyCreateOrUpdate(entity)
doAdditionalValidation(entity)
repo.saveAndFlush(entity)
}
Вот как выглядит метод saveAndFlush с использованием аннотации @Secured Spring Security:
@Secured([Roles.SOME_ENTITY_CREATE,Roles.SOME_ENTITY_UPDATE])
SomeEntity saveAndFlush(SomeEntity entity)
Пользовательский AccessDecisionManager должен вызывать RoleVoter, выполнять итерацию пороли метода и сравнить их с ролями пользователя.Но этот избиратель никогда не вызывается.
Локально, я могу видеть общий стек вызовов, который приводит к вызову пользовательского AccessDecisionManager.После вызова saveAndFlush мы попадаем в JdkDynamicAopProxy.invoke ().В этом методе происходит следующее:
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Локально этот прокси получает дважды .При первом вызове цепочка содержит наш собственный перехватчик методов со всеми нашими избирателями.Во второй раз, когда он вызывается, он содержит группу других перехватчиков, которые были добавлены RepositoryFactorySupport при запуске приложения.
При работе в наших более высоких средах этот прокси-сервер вызывается только один раз , ицепочка перехватчиков содержит только перехватчики, добавленные RepositoryFactorySupport.Он не вызывается снова для вызова saveAndFlush.
После многих часов удаленной отладки я не смог выяснить, что является причиной этого.Мой инстинкт говорит мне, что, поскольку не совпадает между нашим приложением, работающим локально, и нашим приложением, работающим на dev, это проблема весеннего профиля.Но я так и не смог выяснить , что в профилях пружин может быть причиной этого.
Вот несколько особенностей относительно версий, которые мы используем:
- openjdk : 8u171
- tomcat : 8.5.32
- изображение докера : tomcat: 8.5.32-jre8-alpine
- springBootVersion : 1.4.2.RELEASE
- groovy : 2.4.7
IПонимаю, что эта проблема является раздражающей и туманной, и все это похоже на красную сельдь или симптом совершенно другой проблемы.Честно говоря, я ищу любой совет для того, что еще я мог бы сделать, чтобы даже попытаться выяснить, в чем проблема на самом деле .
Спасибо за чтение.