Using OAuth2RestTemplate in multi-thread context

Viewed 4480

I'm using OAuth2RestTemplate in my Spring boot application and work with some resource through it as it encapsulates all the authentication info, so i can just send the requests, not worrying about tokens and other auth stuff.

That all works fine until i send the requests in parallel.

Because of the OAuth2RestTemplate has a Session scope (which is local as it contains user's session-related info), when i'm trying to use it in multi-thread environment, i get the following exception

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.oauth2ClientContext': Scope 'session' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.

As I understand, this happens because these separate threads, where the code is being executed, are not wired with session.

The only solution i found for now is binding the session with new threads manually in code, but it i don't like it.

    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

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

    RequestContextHolder.setRequestAttributes(requestAttributes);

There's a ticket on Spring Jira where similar issue were discussed but still i still hope that there's some solution related to OAuth2RestTemplate.

So i'm wondering if anyone meet this and how you resolved it.

2 Answers

I was able to easily make use of OAuth2RestTemplate in multiple threads after I created my own bean, and made sure it was autowired correctly.

Before I was getting the exact same error message and it turned out that I didn't get the OAuth2RestTemplate I had created before:

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

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

    return restTemplate;
}

In particular, the request factory wasn't present in the RestTemplate that got autowired, in fact it was null. Moreover, I actually had multiple different OAuth2RestTemplate beans, but @Autowired returned the exact same instance for all of them.

The key turned out to be in OAuth2RestOperationsConfiguration:

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

Because of the @Primary annotation, all @Autowired instances apparently will get this one. Specifying the bean you want to get by parameter name, what usually worked before, didn't so in this case.

Adding a @Qualifier annotation resolved this issue:

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

Now, the correct instance is returned and when making a request, the auth server is called to get an OAuth2 token, as configured in the application configuration in security.oauth2.client.

However, I didn't attempt to autowire the RestTemplate from within a new thread. Instead, I pass it in as an argument. I'm not completely sure if this makes a difference, but since getAccessToken() is called by the RestTemplate during request execution, I assume it should also work when autowired into the new thread.

Related