2

Я использую OAuth2RestTemplate в моем приложении загрузки Spring и работаю с каким-то ресурсом через него, поскольку он инкапсулирует всю информацию аутентификации, поэтому я могу просто отправлять запросы, не беспокоясь о токенах и других auth вещи.Использование OAuth2RestTemplate в многопоточном контексте

Это все работает нормально, пока я не отправлю запросы параллельно.

Из-за OAuth2RestTemplate имеет Session объем (который является локальным, поскольку она содержит пользователь данные сеанса, связанные с), когда я пытаюсь использовать его в среде многопоточной, я получаю следующее исключение

org.springframework.beans.factory.BeanCreationException: Ошибка создания bean-компонента с именем 'scopedTarget.oauth2ClientContext': Scope 'session' неактивен для текущего потока; рассмотрите определение прокси-объекта с областью действия для этого компонента, если вы намерены ссылаться на него из одноэлементного; Вложенное исключение - это java.lang.IllegalStateException: запрос на привязку к потоку не найден: ссылаетесь ли вы на атрибуты запроса вне фактического веб-запроса или обрабатываете запрос за пределами исходного потока? Если вы действительно работаете в веб-запросе и все еще получите это сообщение, ваш код, вероятно, работает за пределами DispatcherServlet/DispatcherPortlet: В этом случае используйте RequestContextListener или RequestContextFilter, чтобы выставить текущий запрос .

Как я понимаю, это происходит потому, что эти отдельные потоки, где выполняется код, не связаны сеансом.

Единственное решение, которое я нашел сейчас, - привязка сеанса с новыми потоками вручную в коде, но мне это не нравится.

RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); 

    Stream.of(1, 2, 3).parallel().forEach(it -> { 
     RequestContextHolder.setRequestAttributes(requestAttributes); 
     //do something 
     RequestContextHolder.resetRequestAttributes(); 
    }); 

    RequestContextHolder.setRequestAttributes(requestAttributes); 

Там в ticket on Spring Jira где обсуждались аналогичный вопрос, но до сих пор я все еще надеюсь, что есть какое-то решение связано с OAuth2RestTemplate.

Так что мне интересно, если кто-нибудь встретит это и как вы его разрешили.

ответ

0

Для использования области сеанса или запроса вам необходимо предоставить RequestContextListener в качестве компонента в вашем классе @Configuration. См. Например, this thread.

0

Я смог легко использовать OAuth2RestTemplate в нескольких потоках после того, как создал собственный фасоль, и убедился, что он был правильно настроен.

Прежде чем я получаю точно такое же сообщение об ошибке, и оказалось, что я не получил OAuth2RestTemplate я создан до:

@Bean 
@Autowired 
public OAuth2RestTemplate oAuth2RestTemplate(
    OAuth2ProtectedResourceDetails resourceDetails, 
    SpringClientFactory clientFactory) { 

    OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceDetails); 
    restTemplate.setRequestFactory(new RibbonClientHttpRequestFactory(clientFactory)); 

    return restTemplate; 
} 

В частности, запрос завод не присутствовал в RestTemplate который получил автоволну, на самом деле это было null. Более того, у меня на самом деле было несколько разных фанов OAuth2RestTemplate, но @Autowired вернул тот же самый экземпляр для всех из них.

Ключ оказался в OAuth2RestOperationsConfiguration:

@Bean 
@Primary 
public OAuth2RestTemplate oauth2RestTemplate(OAuth2ClientContext oauth2ClientContext, 
     OAuth2ProtectedResourceDetails details) { 
    OAuth2RestTemplate template = new OAuth2RestTemplate(details, 
      oauth2ClientContext); 
    return template; 
} 

Из-за @Primary аннотацию, все @Autowired экземпляры, видимо, получит это. Указание компонента, который вы хотите получить по имени параметра, обычно работало до этого, в этом случае не так.

Добавление @Qualifier аннотацию решить эту проблему:

@Autowired 
@Qualifier("myOwnOAuth2RestTemplate") 
OAuth2RestTemplate oAuth2RestTemplate; 

Теперь, правильный экземпляр возвращается и при создании запроса, сервер аутентификации называется, чтобы получить маркер OAuth2, в соответствии с настройками в конфигурации приложения в security.oauth2.client.

Однако, я не пытаюсь автоувеличивать RestTemplate из новой темы. Вместо этого я передаю его в качестве аргумента. Я не совсем уверен, если это имеет значение, но так как getAccessToken() вызывается RestTemplate во время выполнения запроса, я предполагаю, что он также должен работать при автоподключении к новому потоку.

+0

вы сделали одноэлементный боб, чтобы он работал нормально, потому что Spring может обрабатывать одиночные силовые линии для разных потоков. Но проблема здесь в том, что oAuth2RestTemplate по умолчанию имеет область сеанса для хранения связанной с пользователем информации об аутентификации, поэтому каждый пользователь может использовать собственный компонент. Я не уверен, что это будет корректно работать с Singleton oAuth2RestTemplate. – ikryvorotenko

+0

@ikryvorotenko Учитывая, что вы хотите использовать маркер доступа OAuth2 аутентифицированного принципала в «SecurityContext», вам фактически не нужен отдельный компонент для каждого пользователя. Вы можете обновить «OAuth2ClientContext» с помощью токена из «Аутентификация» в «SecurityContext». И я думаю, что бонусы с сессией не являются «настоящими» синглонами из-за магии прокси-сервера Spring в конце. На самом деле, мы успешно используем различные компоненты 'OAuth2RestTemplate' на основе подхода, описанного в моем ответе, а также дополнительные компоненты, которые используют статические учетные данные для имени пользователя и пароля для получения токена. –

Смежные вопросы