I'm trying to implement digest auth with an HTTP client, but this does not work at the moment.
Can someone check if this code is correct? For testing purpose I use http://httpbin.org/, but all I get is HTTP/1.1 401 Unauthorized.
Here is the example code:
private static void doDigestAuth() throws ClientProtocolException,
IOException,
AuthenticationException,
MalformedChallengeException
{
HttpHost target = new HttpHost("httpbin.org", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope(target.getHostName(), target.getPort()), new UsernamePasswordCredentials(
"user", "passwd"));
CloseableHttpClient httpclient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
try {
HttpGet httpget = new HttpGet("http://httpbin.org/digest-auth/auth/user/passwd");
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate DIGEST scheme object, initialize it and add it to the local
// auth cache
DigestScheme digestAuth = new DigestScheme();
// Suppose we already know the realm name
digestAuth.overrideParamter("realm", "me#kennethreitz.com");
// // Suppose we already know the expected nonce value
// digestAuth.overrideParamter("nonce", Long.toString(new SecureRandom().nextLong(), 36));
// qop-value = "auth" | "auth-int" | token
digestAuth.overrideParamter("qop", "auth");
authCache.put(target, digestAuth);
// Add AuthCache to the execution context
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
// context.setAuthSchemeRegistry(authRegistry);
context.setAuthCache(authCache);
System.out.println("Executing request " + httpget.getRequestLine() + " to target " + target);
for (int i = 0; i < 3; i++) {
CloseableHttpResponse response = httpclient.execute(httpget, context);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
InputStream instream = entity.getContent();
// Header contentCncoding = entity .getContentEncoding();
String contentString = IOUtils.toString(instream, null);
System.out.println("ContentString:" + contentString);
AuthState proxyAuthState = context.getProxyAuthState();
System.out.println("Proxy auth state: " + proxyAuthState.getState());
System.out.println("Proxy auth scheme: " + proxyAuthState.getAuthScheme());
System.out.println("Proxy auth credentials: " + proxyAuthState.getCredentials());
AuthState targetAuthState = context.getTargetAuthState();
System.out.println("Target auth state: " + targetAuthState.getState());
System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
System.out.println("Target auth credentials: " + targetAuthState.getCredentials());
EntityUtils.consume(response.getEntity());
}
finally {
response.close();
}
}
}
finally {
httpclient.close();
}
}
It was a cookie problem as #heaphach suggested. The wire-log (shown with log category org.apache.http.wire set to debug) shows:
<< "Set-Cookie: fake=fake_value[\r][\n]"
but the HttpClient never picks this up
and does not use it in the second GET request containing the full "Authorization" header with the digest-response.
As a consequence, the server just ignores the digest response.
After I updated the example code (also known as the Preemptive DIGEST authentication example)
with the code shown below (copied from the HTTP state management tutorial), the server responded "200 OK".
CookieStore cookieStore = new BasicCookieStore();
BasicClientCookie cookie = new BasicClientCookie("fake", "fake_value");
cookie.setDomain("httpbin.org");
cookie.setPath("/");
cookieStore.addCookie(cookie);
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.setDefaultCredentialsProvider(credsProvider)
.build();
I also came across a gist containing some code to calculate a "nonce"
so you can use
digestAuth.overrideParamter("nonce", calculateNonce());
and org.apache.http.impl.auth.HttpAuthenticator no longer shows the error message "missing nonce in challenge".
public static synchronized String calculateNonce() {
Date d = new Date();
SimpleDateFormat f = new SimpleDateFormat("yyyy:MM:dd:hh:mm:ss");
String fmtDate = f.format(d);
Random rand = new Random(100000);
Integer randomInt = rand.nextInt();
return org.apache.commons.codec.digest.DigestUtils.md5Hex(fmtDate + randomInt.toString());
}
Related
I am calling a restful api using HttpClient 4.4.1, but it is not sending the cookies,
private CloseableHttpResponse call(String url, javax.servlet.http.HttpServletRequest httpServletRequest) {
HttpGet request = new HttpGet(url);
BasicHttpContext localContext = new BasicHttpContext();
CookieStore cookieStore = new BasicCookieStore();
javax.servlet.http.Cookie[] cookies = httpServletRequest.getCookies();
BasicClientCookie basicClientCookie = null;
if (cookies != null) {
for (int i = 0; i < cookies.length; i++) {
javax.servlet.http.Cookie cookie = cookies[i];
basicClientCookie = new BasicClientCookie(cookie.getName(), cookie.getValue());
basicClientCookie.setDomain(cookie.getDomain());
basicClientCookie.setPath("/");
basicClientCookie.setAttribute(ClientCookie.DOMAIN_ATTR, "true");
basicClientCookie.setVersion(0);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, 100);
Date date = calendar.getTime();
basicClientCookie.setExpiryDate(date);
cookieStore.addCookie(basicClientCookie);
}
}
if (cookieStore.getCookies() != null) {
System.out.println("Cookies size " + cookieStore.getCookies().size());
}
localContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore);
CloseableHttpClient httpClient = HttpClients.createDefault();
CloseableHttpResponse httpResponse = null;
try {
httpResponse = httpClient.execute(request, localContext);
} catch (IOException e) {
System.out.println("msg " + e.getMessage());
}
return httpResponse;
}
I can see it is only sending the last cookie which has been added. What am i missing? Please help.
The problem is for sure in the code inside the for loop because only the last added cookie is visible.
Try to debug the for loop either using debugger or by adding the system.out.println statement before the loop when you are getting cookies array from request, than inside the loop when you are creating basic client cookie and then before and after adding basic client cookie to cookiestore
As suggested in the comments you have also not added cookiestore to the context.
localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
Another problem with the above code is
basicClientCookie.setDomain(cookie.getDomain());
cookie.getDomain() and url of the request can be different and as per the cookies behaviour they should be same then only cookies will send.So only cookies having same url will be send with the request
I am trying to upload a file by the POST method with the Apache HttpClient library.
I used the example code for the preemptive basic authentification here:
package ahcs;
// many imports, press ctrl-o in eclipse
public class App {
static final String url = "http://127.0.0.1:64738/test/";
static final String content = "test\nfile\ndata";
static final String httpUser = "testuser";
static final String httpPasswd = "testPassword";
static final String fileUploadFieldName = "uploadData";
static final String fileName = "upload.dat";
public static void main(String[] args) {
System.err.println("Uploading to URL " + url);
CloseableHttpClient httpclient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(url);
httpPost.setProtocolVersion(HttpVersion.HTTP_1_1);
MultipartEntityBuilder mpEntityBuilder =
MultipartEntityBuilder.create();
mpEntityBuilder.setMode(HttpMultipartMode.RFC6532);
mpEntityBuilder.addBinaryBody(fileUploadFieldName,
content.getBytes(), ContentType.DEFAULT_BINARY, fileName);
httpPost.setEntity(mpEntityBuilder.build());
System.err.println("executing request " + httpPost.getRequestLine());
HttpEntity resEntity = null;
try {
// Really simple HTTP Authentification, grat Apache
HttpHost httpHost = URIUtils.extractHost(new URI(url));
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(httpUser, httpPasswd));
AuthCache authCache = new BasicAuthCache();
authCache.put(httpHost, new BasicScheme());
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);
HttpResponse response = httpclient.execute(httpPost);
resEntity = response.getEntity();
System.err.println(response.getStatusLine().toString());
if (resEntity != null) {
System.err.println(EntityUtils.toString(resEntity));
}
int status = response.getStatusLine().getStatusCode();
if (status != HttpStatus.SC_OK) {
throw new HttpResponseException(status,
"Upload error! (" + status + ")");
}
EntityUtils.consume(resEntity);
httpclient.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
Unfortunately, it doesn't do what I want. The request what the apache httpclient gives, is this (I got this by listening from the command line with an nc -p 64738 -l command):
POST /test/ HTTP/1.1
Content-Length: 249
Content-Type: multipart/form-data; boundary=PIrvSJ07MLxTV2rC4d-5ZfoL3CvJFJdJqO4i
Host: 127.0.0.1:64738
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.5.4 (Java/1.8.0_151)
Accept-Encoding: gzip,deflate
--PIrvSJ07MLxTV2rC4d-5ZfoL3CvJFJdJqO4i
Content-Disposition: form-data; name="uploadData"; filename="upload.dat"
Content-Type: application/octet-stream
Content-Transfer-Encoding: binary
test
file
data
--PIrvSJ07MLxTV2rC4d-5ZfoL3CvJFJdJqO4i--
As we can see, everything is okay, except that the authentification header is simply missing.
Why is it so? What is the bug?
According to RFC7617 you need only one header "Authorization" with values "Basic " + login:passord in Base64 encoding to successefuly pass Basic authorization.
Your code is correct, except one thing - when you call httpPost.execute you are not pass execution context, and AuthCache and CredentialsProvider wasn't used at all.
package ahcs;
// many imports, press ctrl-o in eclipse
public class App {
static final String url = "http://127.0.0.1:64738/test/";
static final String content = "test\nfile\ndata";
static final String httpUser = "testuser";
static final String httpPasswd = "testPassword";
static final String fileUploadFieldName = "uploadData";
static final String fileName = "upload.dat";
public static void main(String[] args) {
System.err.println("Uploading to URL " + url);
CloseableHttpClient httpclient = HttpClientBuilder.create().build();
HttpPost httpPost = new HttpPost(url);
httpPost.setProtocolVersion(HttpVersion.HTTP_1_1);
MultipartEntityBuilder mpEntityBuilder =
MultipartEntityBuilder.create();
mpEntityBuilder.setMode(HttpMultipartMode.RFC6532);
mpEntityBuilder.addBinaryBody(fileUploadFieldName,
content.getBytes(), ContentType.DEFAULT_BINARY, fileName);
httpPost.setEntity(mpEntityBuilder.build());
System.err.println("executing request " + httpPost.getRequestLine());
HttpEntity resEntity = null;
try {
// Really simple HTTP Authentification, grat Apache
HttpHost httpHost = URIUtils.extractHost(new URI(url));
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials(httpUser, httpPasswd));
AuthCache authCache = new BasicAuthCache();
authCache.put(httpHost, new BasicScheme());
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);
// context was missed
HttpResponse response = httpclient.execute(httpPost, context);
resEntity = response.getEntity();
System.err.println(response.getStatusLine().toString());
if (resEntity != null) {
System.err.println(EntityUtils.toString(resEntity));
}
int status = response.getStatusLine().getStatusCode();
if (status != HttpStatus.SC_OK) {
throw new HttpResponseException(status,
"Upload error! (" + status + ")");
}
EntityUtils.consume(resEntity);
httpclient.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
But for Basic Auth using this API may be a bit verbose, it was designed to support many different authorization schemes.
If you know what charset server will use to decode Authorization header (suppose it UTF-8), you can write one-liner:
httpPost.setHeader("Authorization", "Basic " + Base64.getEncoder().encodeToString((httpUser + ':' + httpPasswd).getBytes("UTF-8")));
I am running the sample Apache hc (http client) for digest authentication. I didn't change anything, just using the provided sample:
public static void main(String[] args) throws Exception {
HttpHost target = new HttpHost("httpbin.org", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(target.getHostName(), target.getPort()),
new UsernamePasswordCredentials("user", "passwd"));
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.build();
try {
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate DIGEST scheme object, initialize it and add it to the local
// auth cache
DigestScheme digestAuth = new DigestScheme();
// Suppose we already know the realm name
digestAuth.overrideParamter("realm", "me#kennethreitz.com");
// Suppose we already know the expected nonce value
digestAuth.overrideParamter("nonce", "b2c603bb7c93cfa197945553a1044283");
authCache.put(target, digestAuth);
// Add AuthCache to the execution context
HttpClientContext localContext = HttpClientContext.create();
localContext.setAuthCache(authCache);
HttpGet httpget = new HttpGet("http://httpbin.org/digest-auth/auth/user/passwd");
System.out.println("Executing request " + httpget.getRequestLine() + " to target " + target);
for (int i = 0; i < 3; i++) {
CloseableHttpResponse response = httpclient.execute(target, httpget, localContext);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
} finally {
response.close();
}
}
} finally {
httpclient.close();
}
}
And I am getting: HTTP/1.1 401 UNAUTHORIZED
If I go direct to http://httpbin.org/digest-auth/auth/user/passwd in prompts me for user/passwd and then provides the page. So the website is working right.
Any idea what is wrong? I have the latest version of the library.
Fiddler Auth for browser (successful):
No Proxy-Authorization Header is present.
Authorization Header is present: Digest username="user",
realm="me#kennethreitz.com", nonce="8ada87344eb5a10bf810bcc211205c24",
uri="/digest-auth/auth/user/passwd",
response="ad22423e5591d14c90c6fe3cd762e64c",
opaque="361645844d957289c4c8f3479f76269f", qop=auth, nc=00000001,
cnonce="260d8ddfe64bf32e"
Fiddler Auth for my code (failed):
No Proxy-Authorization Header is present.
Authorization Header is present: Digest username="user",
realm="me#kennethreitz.com", nonce="76af6c9c0a1f57ee5f0fcade2a5f758c",
uri="http://httpbin.org/digest-auth/auth/user/passwd",
response="745686e3f38ab40ce5907d41f91823e6", qop=auth, nc=00000001,
cnonce="634b618d5c8ac9af", algorithm=MD5,
opaque="fe84ce11c48a7b258490600800e5e6df"
This code digestAuth.overrideParamter("realm", "some realm") should have some change.To replace "some realm" by your server realm.Please look this question
Ok I got it working. You have to set a cookie too. Thanks to this post for the help. The below code works - but only if you are not using Fiddler.
public static void main(String[] args) throws Exception {
CookieStore cookieStore = new BasicCookieStore();
BasicClientCookie cookie = new BasicClientCookie("fake", "fake_value");
cookie.setDomain("httpbin.org");
cookie.setPath("/");
cookieStore.addCookie(cookie);
// https://stackoverflow.com/questions/27291842/digest-auth-with-java-apache-client-always-401-unauthorized
HttpHost target = new HttpHost("httpbin.org", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(target.getHostName(), target.getPort()),
new UsernamePasswordCredentials("user", "passwd"));
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.setDefaultCredentialsProvider(credsProvider)
// .setProxy(new HttpHost("127.0.0.1", 8888))
.build();
try {
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate DIGEST scheme object, initialize it and add it to the local
// auth cache
DigestScheme digestAuth = new DigestScheme();
// Suppose we already know the realm name
digestAuth.overrideParamter("realm", "me#kennethreitz.com");
// Suppose we already know the expected nonce value
digestAuth.overrideParamter("nonce", calculateNonce());
authCache.put(target, digestAuth);
// Add AuthCache to the execution context
HttpClientContext localContext = HttpClientContext.create();
localContext.setAuthCache(authCache);
HttpGet httpget = new HttpGet("http://httpbin.org/digest-auth/auth/user/passwd");
System.out.println("Executing request " + httpget.getRequestLine() + " to target " + target);
CloseableHttpResponse response = httpclient.execute(target, httpget, localContext);
try {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
System.out.println(EntityUtils.toString(response.getEntity()));
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
public static synchronized String calculateNonce() {
Date d = new Date();
SimpleDateFormat f = new SimpleDateFormat("yyyy:MM:dd:hh:mm:ss");
String fmtDate = f.format(d);
Random rand = new Random(100000);
Integer randomInt = rand.nextInt();
return org.apache.commons.codec.digest.DigestUtils.md5Hex(fmtDate + randomInt.toString());
}
HttpHost targetHost = new HttpHost("myhost",8080, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new
AuthScope(targetHost.getHostName(), targetHost.getPort()), new
UsernamePasswordCredentials("username", "password"));
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put(targetHost, basicAuth);
// Add AuthCache to the execution context
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
context.setAuthCache(authCache);
HttpGet httpget = new HttpGet("/");
try {
HttpResponse response = httpclient.execute(targetHost, httpget, context);
System.out.println(httpclient.getCookieStore().getCookies());
} catch (Exception e) {
} finally {
try {
httpget.abort();}catch(Exception e){}
}
}
but output i am getting is : [] nothing else . what mistake i am doing and how i can get jsessionId so that i can store it and use it later when i have to post json data to my server
Have you checked this link
How to manage sessions with Android Application
private void parseSessionID(HttpResponse response) {
try {
Header header = response.getFirstHeader("Set-Cookie");
String value = header.getValue();
if (value.contains("JSESSIONID")) {
int index = value.indexOf("JSESSIONID=");
int endIndex = value.indexOf(";", index);
String sessionID = value.substring(
index + "JSESSIONID=".length(), endIndex);
Logger.d(this, "id " + sessionID);
if (sessionID != null) {
classStaticVariable= sessionID;
}
}
} catch (Exception e) {
}
i'm trying to send a https request through a proxy with apache httpclient,but i can't find the headers on the proxy side
HttpClient httpClient =new DefaultHttpClient();
HttpHost proxy = new HttpHost("10.1.1.100", 8080);
httpClient.getParams().setParameter(ConnRouteParams.DEFAULT_PROXY,proxy);
HttpGet get = new HttpGet(uri);
get.addHeader("Proxy-Authorization", "222222");
HttpResponse hr = defaultHttpClient.execute(get);
the proxy side only find proxy-connection and user-agent:
Proxy-Connection:[Keep-Alive] User-Agent:[Apache-HttpClient/4.3.6 (java 1.5)]
First, that's not how you authenticate to a proxy. Second, those headers are added to the get request (not to the proxy). Finally, this is based on an example the HttpClient examples - specifically ClientProxyAuthentication and updated to use try-with-resources (and modified to use an URL)
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope("10.1.1.100", 8080),
new UsernamePasswordCredentials("username", "password"));
try (CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider).build()) {
URL url = new URL(uri);
HttpHost target = new HttpHost(url.getHost(), url.getPort(),
url.getProtocol());
HttpHost proxy = new HttpHost("10.1.1.100", 8080);
RequestConfig config = RequestConfig.custom().setProxy(proxy)
.build();
HttpGet httpget = new HttpGet(url.getPath());
httpget.setConfig(config);
System.out.println("Executing request " + httpget.getRequestLine()
+ " to " + target + " via " + proxy);
try (CloseableHttpResponse response = httpclient.execute(target,
httpget)) {
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
EntityUtils.consume(response.getEntity());
}
} catch (IOException e1) {
e1.printStackTrace();
}