-
Notifications
You must be signed in to change notification settings - Fork 227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
401 when Server responds with multiple WWW-Authenticate due to HTTPCLIENT-1489 #133
Comments
Here comes my workaround. Specify a class: import org.apache.http.FormattedHeader;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.auth.AUTH;
import org.apache.http.auth.MalformedChallengeException;
import org.apache.http.impl.client.TargetAuthenticationStrategy;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.Args;
import org.apache.http.util.CharArrayBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* Workaround for <a href="https://jira.apache.org/jira/browse/HTTPCLIENT-1489">HTTPCLIENT-1489</a>.
*/
public class HttpClient1489TargetAuthenticationStrategy extends TargetAuthenticationStrategy {
private final String challengeName;
public HttpClient1489TargetAuthenticationStrategy(String challengeName) {
this.challengeName = challengeName;
}
/**
* Generates a map of challenge auth-scheme => Header entries.
*
* @return map: key=lower-cased auth-scheme name, value=Header that contains the challenge
*/
@Override
public Map<String, Header> getChallenges(
final HttpHost authhost,
final HttpResponse response,
final HttpContext context
) throws MalformedChallengeException {
Args.notNull(response, "HTTP response");
final Header[] headers = filterChallenge(response.getHeaders(AUTH.WWW_AUTH), challengeName);
final Map<String, Header> map = new HashMap<>(headers.length);
for (final Header header : headers) {
final CharArrayBuffer buffer;
int pos;
if (header instanceof FormattedHeader) {
buffer = ((FormattedHeader) header).getBuffer();
pos = ((FormattedHeader) header).getValuePos();
} else {
final String s = header.getValue();
if (s == null) {
throw new MalformedChallengeException("Header value is null");
}
buffer = new CharArrayBuffer(s.length());
buffer.append(s);
pos = 0;
}
while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) {
pos++;
}
final int beginIndex = pos;
while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) {
pos++;
}
final int endIndex = pos;
final String s = buffer.substring(beginIndex, endIndex);
map.put(s.toLowerCase(Locale.ROOT), header);
}
return map;
}
/**
* Removes all but the specified {@code WWW-Authenticate} challenge.
* <p>
* For instance, the header:
* <pre>
* WWW-Authenticate: X-MobileMe-AuthToken realm="Newcastle", Basic realm="Newcastle"
* </pre>
* becomes:
* <pre>
* WWW-Authenticate: X-MobileMe-AuthToken realm="Newcastle"
* </pre>
* if this class has been instantiated with "X-MobileMe-AuthToken" or:
* <pre>
* WWW-Authenticate: Basic realm="Newcastle
* </pre>
* if this class has been instantiated with "Basic". An exception is thrown if the specified
* challenge could not be found.
* </p>
*/
private Header[] filterChallenge(Header[] headers) {
// CAVEAT: Calling header.getElements() here is prone to error if the base64 string ends with "="
return Arrays.stream(headers)
.map(header -> Arrays.stream(header.getValue().split(","))
.map(String::trim)
.filter(headerElement -> headerElement
.toLowerCase(Locale.US)
.startsWith(challengeName.toLowerCase(Locale.US)))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("There must be exactly one challenge with name '"
+ challengeName + "' in headers: " + Arrays.toString(headers)))
)
.map(headerElement -> new BasicHeader(AUTH.WWW_AUTH, headerElement))
.toArray(Header[]::new);
}
} And create your own private static HttpClient buildHttpClient() {
HttpClientBuilder builder = HttpClientBuilder.create();
Lookup<AuthSchemeProvider> authSchemeRegistry = RegistryBuilder.<AuthSchemeProvider>create()
.register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(true)).build();
builder.setDefaultAuthSchemeRegistry(authSchemeRegistry);
BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(null, -1, null), new NullCredentials());
builder.setDefaultCredentialsProvider(credentialsProvider);
builder.setTargetAuthenticationStrategy(new HttpClient1489TargetAuthenticationStrategy("negotiate"));
return builder.build();
}
private static class NullCredentials implements Credentials {
@Override
public Principal getUserPrincipal() {
return null;
}
@Override
public String getPassword() {
return null;
}
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Since this project uses Apache's httpclient 4.3.3, it suffers from https://jira.apache.org/jira/browse/HTTPCLIENT-1489
The issue has been resolved in httpclient 5.0 Alpha1 but users can't just upgrade because the API is incompatible.
If I come up with a workaround, I'll post it here or create a pull request.
The text was updated successfully, but these errors were encountered: