Is there a way to implement preemptive basic authentication with Apache HttpClient 5.2 using a HttpRequestInterceptor
similar to how it's done here (accepted response) for Apache HttpClient 4?
We use Apache HttpClient both directly and as the RestTemplate underlying Http client. Preemptive auth used to work with HttpClient 4 using HttpRequestInterceptor
(see the accepted response for the question I linked above), but we can't reuse the same code as the AuthScheme
class is now gone.
I tried a couple of things but neither worked (see below). Every first request gets a 401 from the server, the client retries with basic auth and gets a successful response. This is not the behaviour I want though, the first request should add the Authorisation header with Basic auth as it used to.
@Component
public class HttpClientPreemptiveAuthInterceptor implements HttpRequestInterceptor {
@Override
public void process(HttpRequest httpRequest, EntityDetails entityDetails, HttpContext httpContext) throws IOException, HttpException {
// Apparently, we have two options:
// - Set HttpContext AuthCache with Basic auth for the target.
// - Copy org.apache.hc.client5.http.ContextBuilder#preemptiveBasicAuth()
// The above method is available for HttpClientContext creation. Not sure why it doesn't set AuthCache though,
// but instead adds an entry to the AuthExchange map of the given http context.
HttpClientContext httpClientContext = (HttpClientContext) httpContext;
// get the target host from the http context
RouteInfo routeInfo = httpClientContext.getHttpRoute();
if (routeInfo != null) {
HttpHost targetHost = routeInfo.getTargetHost();
AuthExchange authExchange = httpClientContext.getAuthExchange(targetHost);
if (authExchange.getAuthScheme() == null) {
CredentialsProvider credentialsProvider = httpClientContext.getCredentialsProvider();
Credentials credentials = credentialsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()), httpClientContext);
if (credentials == null) {
throw new HttpException("No credentials provided for preemptive authentication");
}
BasicScheme authScheme = new BasicScheme();
authScheme.initPreemptive(credentials);
//authExchange.select(authScheme);
//httpClientContext.setAuthExchange(targetHost, authExchange);
DefaultSchemePortResolver schemePortResolver = DefaultSchemePortResolver.INSTANCE;
httpClientContext.resetAuthExchange(RoutingSupport.normalize(targetHost, schemePortResolver), authScheme);
}
}
/** Second approach
// get the target host from the http context
RouteInfo routeInfo = httpClientContext.getHttpRoute();
if (routeInfo != null) {
HttpHost targetHost = routeInfo.getTargetHost();
// add Basic Auth for the target host (credentials for this host will be selected from the CredentialsProvider)
httpClientContext.resetAuthExchange(targetHost, new BasicScheme());
}
**/
// Third approach
// Create AuthCache instance
// final AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local auth cache
// authCache.put(targetHost, new BasicScheme());
// httpClientContext.setAuthCache(new BasicAuthCache());
}