/** * Copyright 2008 The University of North Carolina at Chapel Hill * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package edu.unc.lib.dl.httpclient; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import org.apache.http.HttpException; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpRequestInterceptor; import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthState; import org.apache.http.auth.Credentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpCoreContext; /** * Utitlity class for common <code>HttpClient</code> operations. */ public class HttpClientUtil { public static final String SHIBBOLETH_GROUPS_HEADER = "isMemberOf"; public static final String FORWARDED_GROUPS_HEADER = "forwardedGroups"; /** * Gets a client that is configured to use HTTP Basic authentication. * * @param urlString * the URL that will eventually be fetched. * @param user * the username to use. * @param pass * the password to use * @return a client object configured to supply credentials to the URL specified by <code>urlString</code>. * @throws IllegalArgumentException * if <code>urlString</code> is not a valid URL */ public static CloseableHttpClient getAuthenticatedClient(String urlString, String user, String pass) { return getAuthenticatedClient(urlString, user, pass, null); } public static CloseableHttpClient getAuthenticatedClient(String urlString, String user, String pass, HttpClientConnectionManager connectionManager) { HttpClientBuilder builder = getAuthenticatedClientBuilder(urlString, user, pass); if (connectionManager != null) { builder.setConnectionManager(connectionManager); } return builder.build(); } public static HttpClientBuilder getAuthenticatedClientBuilder(String host, String user, String pass) { final CredentialsProvider credsProvider = new BasicCredentialsProvider(); AuthScope scope = null; if (host == null || host.length() == 0) { scope = new AuthScope(AuthScope.ANY); } else { scope = new AuthScope(new HttpHost(host)); } credsProvider.setCredentials(scope, new UsernamePasswordCredentials(user, pass)); HttpClientBuilder builder = HttpClients.custom() .useSystemProperties() .setDefaultCredentialsProvider(credsProvider) .addInterceptorFirst(new PreemptiveAuthInterceptor()); return builder; } static class PreemptiveAuthInterceptor implements HttpRequestInterceptor { public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { final AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE); // If no auth scheme available yet, try to initialize it // preemptively if (authState.getAuthScheme() == null) { final CredentialsProvider credsProvider = (CredentialsProvider) context .getAttribute(HttpClientContext.CREDS_PROVIDER); final HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST); final AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort()); final Credentials creds = credsProvider.getCredentials(authScope); if (creds == null) { throw new HttpException("No credentials for preemptive authentication"); } authState.update(new BasicScheme(), creds); } } } public static CredentialsProvider getCredentialsProvider(String queryURL, String user, String pass) { CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials(getAuthenticationScope(queryURL), new UsernamePasswordCredentials(user, pass)); return credsProvider; } /** * Generates a limited authentication scope for the supplied URL, so that an HTTP client will not send username and * passwords to other URLs. * * @param queryURL * the URL for the query. * @return an authentication scope tuned to the requested URL. * @throws IllegalArgumentException * if <code>queryURL</code> is not a well-formed URL. */ public static AuthScope getAuthenticationScope(String queryURL) { if (queryURL == null) { throw new NullPointerException("Cannot derive authentication scope for null URL"); } try { URL url = new URL(queryURL); // port defaults to 80 unless the scheme is https // or the port is explicitly set in the URL. int port = 80; if (url.getPort() == -1) { if ("https".equals(url.getProtocol())) { port = 443; } } else { port = url.getPort(); } return new AuthScope(url.getHost(), port); } catch (MalformedURLException mue) { throw new IllegalArgumentException("supplied URL <" + queryURL + "> is ill-formed:" + mue.getMessage()); } } }