SecurityContextHolder возвращает неверное имя пользователя - PullRequest
0 голосов
/ 16 июня 2020

При тестировании конечных точек REST с использованием Mock MVC Spring SecurityContextHolder иногда возвращает неправильное имя пользователя в рамках одного и того же теста. У меня есть служба, в которой есть метод, который возвращает имя пользователя, а репозиторий JPA проверяет, существует ли пользователь (перефразировано):

String username = SecurityContextHolder.getContext().getAuthentication().getName();
Optional<RemoteUser> foundUser = userRepository.findOneByUsername(username);

Тесты при необходимости помечаются @WithMockUser(username = "..." roles="..."). После этого каждый тест прерывается, и контекст приложения Spring обновляется.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = IntegrationTestApplication.class)
public abstract class IntegrationTest {

    ...


    @Resource
    private WebApplicationContext applicationContext;

    protected MockMvc mockMvc;

    ...


    @Before
    public void setUp() {
         mockMvc = MockMvcBuilders.webAppContextSetup(applicationContext).build();
    }

    ...
}

Примерно 97-98% времени тесты проходят нормально. Однако иногда имя пользователя, возвращаемое объектом Authentication, не совпадает с тем, которое было определено в аннотации @WithMockUser. Используя операторы журнала, я даже видел имена пользователей, используемые в других тестовых классах , которые уже выполняли . Перед каждым тестом настраиваются пользователи базы данных, поэтому, если возвращается имя пользователя, которого нет в базе данных, тест завершится неудачно в зависимости от случая, для которого пользователь нужен.

Еще более странно то, что этот метод в этом сервисе можно вызывать несколько раз в течение теста, и иногда имя пользователя правильное, а потом внезапно оказывается неверным. Я не понимаю, как такое возможно. Насколько я понимаю, bean-компонент SecurityContextHolder был потокобезопасным, и поэтому непостоянство тестов сбивает меня с толку. Как это может происходить?

Еще несколько замечаний:

  1. Использование Spring Boot 2.2.7
  2. Тесты не выполняются параллельно.
  3. Вышеупомянутая служба всегда лениво вводится через внедрение поля (@Lazy). Я не знаю, важна ли это деталь, но это не мой код, и это единственная служба, которая использует эту аннотацию, и почему это делается таким образом, мне неизвестно.
  4. Есть несколько методов, помеченных @Async, и я подумал, что, возможно, могут быть побочные эффекты, но у меня нет ничего, чтобы поддержать это, и эта линия мышления - чистое плевание.
  5. Я отлаживал TestSecurityContextHolder и видел, что методы которые очищают контекст, действительно вызываются.
  6. Я видел это SecurityContextHolder также дает неправильные данные о пользователе , но во-первых, нет ответа на вопрос, а во-вторых, нет одновременных запросы в моих тестовых примерах.

1 Ответ

0 голосов
/ 17 июня 2020

Посмотрев еще немного на bean-компонент SecurityContextHolder, я нашел дополнительную информацию о различных доступных стратегиях в отношении потоков. Я обнаружил bean-компонент, который устанавливает это:

SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);

Это распространит контекст безопасности на все потоки, порожденные из основного потока. Его предполагалось исключить из тестового контекста, но этого не произошло. После удаления этого bean-компонента из контекста тестового приложения один тестовый пример, который раньше давал сбои лишь с перерывами, теперь терпел неудачу каждый раз.

Короче говоря, метод службы, использующий объект SecurityContextHolder, вызывался несколько раз внутри параллельного потока . Это привело к следующему: Нулевой участник при получении контекста безопасности в parallelStream . Рефакторинг, чтобы вызвать его только один раз за пределами потока, похоже, решил проблему.

...