I am actually stuck in a data fetching API. In its documentation it has been mentioned that I have to make a call to the login API first then using its Authorization header and cookie (returned in Login API response) I have to make subsequent calls (keeping session control).
The first call is successful and I have received the Cookie i.e. X-SESSIONID and Authorization Header from response Headers. But the subsequent call is returning: 401 Unauthorized even I am passing session Id and authorization header.
May be I am doing something wrong in session management or making subsequent call. Can someone help?
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.core.*;
import org.apache.commons.codec.digest.*;
import org.codehaus.jettison.json.*;
import com.sun.jersey.api.*;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
public class DigestClient {
// Dividing into two parts because we need to send the last part of uri in our
// second request to service.
static String baseUri = "http://data.crea.ca";
static String subUri = "/Login.svc/Login";
public static void main(String[] args) throws JSONException, IOException {
ClientConfig cc = new DefaultClientConfig();
Client client = Client.create(cc);
WebResource webResource = client.resource(baseUri + subUri);
ClientResponse response = webResource.get(ClientResponse.class);
// Basically in Digest-Authentication mechanism, we hit the rest service two
// times.
// First time with No Authentication, which returns some values (qop, nonce,
// realm) which are used as inputs in second call to rest service.
/*--------------- First call-----------------*/
// We get 401, Unauthorized
System.out.println(response.getStatus() );
// Here is the complete header information
System.out.println(response.getHeaders());
// We need "WWW-Authenticate" part information for our second call to rest
System.out.println("WWW-Authenticate: \t" + response.getHeaders().get("www-Authenticate"));
String noAuthResp = response.getHeaders().get("www-Authenticate").toString();
noAuthResp = noAuthResp.replace("Digest ", "");
noAuthResp = noAuthResp.replace('[', '{');
noAuthResp = noAuthResp.replace(']', '}');
// Creating a JSONObject for easy information retrieval
JSONObject resp = new JSONObject(noAuthResp);
/*--------------- Second call-----------------*/
// Here client has to set the fields which was returned from the first call
String user = "CXLHfDVrziCfvwgCuL8nUahC"; // username
String password = "mFqMsCSPdnb5WO1gpEEtDCHH"; // password
String realm = resp.getString("realm"); // realm value from the first rest-call response
String qop = resp.getString("qop"); // qop value from the first rest-call response
String nonce = resp.getString("nonce"); // nonce value from the first rest-call response
String opaque = "";//resp.getString("opaque"); // Some times if we don't get this value, set it with ""
String algorithm = "MD5"; // The algorithm set by the client
int nonceCount = 678; // Some numerical input from the client
String clientNonce = "afdjas0"; // Some random text from the client for encryption
String method = "GET"; // HTTP method
String ha1 = new DigestClient().formHA1(user, realm, password);
String ha2 = new DigestClient().formHA2(method, subUri);
String responseCode = new DigestClient().generateResponse(ha1, nonce, nonceCount, clientNonce, qop, ha2);
// Header to be sent to the service
String value = "Digest username=\"" + user + "\", realm=\"" + realm + "\", nonce=\"" + nonce + "\", uri=\""
+ subUri + "\", qop=" + qop + ", nc=" + nonceCount + ", cnonce=\"" + clientNonce + "\", response=\""
+ responseCode + "\", opaque=\"" + opaque + "\"";
System.out.println("Digest Header: " + value );
// Hitting the service
response = webResource.header("authorization", value).type(MediaType.TEXT_PLAIN).accept("*")
.get(ClientResponse.class);
System.out.println("\nComplete Response:\n" + response + "\n");
String output = response.getEntity(String.class);
MultivaluedMap<String, String> responseHeaders = response.getHeaders();
System.out.println("Response header: " + prepareParameters(responseHeaders).toString());
System.out.println("Cookie: " + response.getCookies().get(1).toString());
// System.out.println("Response Text: " + output);
String sessionId = response.getCookies().get(1).toString();
/****************************SUBSEQUENT CALL (WHERE I AM PASSING SESSION ID AS COOKIE AND AUTHORIZATION HEADER ***************************/
System.out.println("Getting metadata..");
System.out.println("SessionID: " + sessionId.split(";")[0]);
System.out.println("Digest Header: " + value);
String sid = sessionId.split(";")[0];
String request = "<Employee><Name>Sunil</Name></<Employee>";
webResource = client.resource("http://data.crea.ca/Metadata.svc/GetMetadata");
ClientResponse response2 = webResource.get(ClientResponse.class);
response2 = webResource.header("authorization", value).header("Cookie", sid).type(MediaType.APPLICATION_XML).accept("*")
.post(ClientResponse.class, request);
if (response2.getStatus() != 200) {
System.out.println("MetaDataError: " + response2.getStatus());
}
String output2 = response2.getEntity(String.class);
//
// URL url = new URL("http://data.crea.ca/Metadata.svc/GetMetadata");
// HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//
// // Set timeout as per needs
// connection.setConnectTimeout(20000);
// connection.setReadTimeout(20000);
//
// // Set DoOutput to true if you want to use URLConnection for output.
// // Default is false
// connection.setDoOutput(true);
//
// connection.setUseCaches(true);
// connection.setRequestMethod("POST");
//
// // Set Headers
// connection.setRequestProperty("Accept", "application/xml");
// connection.setRequestProperty("Content-Type", "application/xml");
// connection.setRequestProperty("Authorization", value);
// connection.setRequestProperty("Cookie", sid);
//
// // Write XML
// OutputStream outputStream = connection.getOutputStream();
// byte[] b = request.getBytes("UTF-8");
// outputStream.write(b);
// outputStream.flush();
// outputStream.close();
//
// // Read XML
// InputStream inputStream = connection.getInputStream();
// byte[] res = new byte[2048];
// int i = 0;
// StringBuilder response2 = new StringBuilder();
// while ((i = inputStream.read(res)) != -1) {
// response2.append(new String(res, 0, i));
// }
// inputStream.close();
System.out.println("Response of metadata= " + output2.toString());
//getMetadata( response.getCookies().get(1).toString(), value);
}
public static void getMetadata(String sessionId, String header) throws IOException {
System.out.println("Getting metadata..");
System.out.println("SessionID: " + sessionId.split(";")[0]);
System.out.println("Digest Header: " + header);
String sid = sessionId.split(";")[0];
String request = "<Employee><Name>Sunil</Name></<Employee>";
URL url = new URL("http://data.crea.ca/Metadata.svc/GetMetadata");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Set timeout as per needs
connection.setConnectTimeout(20000);
connection.setReadTimeout(20000);
// Set DoOutput to true if you want to use URLConnection for output.
// Default is false
connection.setDoOutput(true);
connection.setUseCaches(true);
connection.setRequestMethod("POST");
// Set Headers
connection.setRequestProperty("Accept", "application/xml");
connection.setRequestProperty("Content-Type", "application/xml");
connection.setRequestProperty("Authorization", header);
connection.setRequestProperty("Cookie", sid);
// Write XML
OutputStream outputStream = connection.getOutputStream();
byte[] b = request.getBytes("UTF-8");
outputStream.write(b);
outputStream.flush();
outputStream.close();
// Read XML
InputStream inputStream = connection.getInputStream();
byte[] res = new byte[2048];
int i = 0;
StringBuilder response = new StringBuilder();
while ((i = inputStream.read(res)) != -1) {
response.append(new String(res, 0, i));
}
inputStream.close();
System.out.println("Response of metadata= " + response.toString());
}
private static Map<String,String> prepareParameters(MultivaluedMap<String, String> queryParameters) {
Map<String,String> parameters = new HashMap<String,String>();
for(String str : queryParameters.keySet()){
parameters.put(str, queryParameters.getFirst(str));
}
return parameters;
}
// For generating HA1 value
public String formHA1(String userName, String realm, String password) {
String ha1 = DigestUtils.md5Hex(userName + ":" + realm + ":" + password);
return ha1;
}
// For generating HA2 value
public String formHA2(String method, String uri) {
String ha2 = DigestUtils.md5Hex(method + ":" + uri);
return ha2;
}
// For generating response at client side
public String generateResponse(String ha1, String nonce, int nonceCount, String clientNonce, String qop,
String ha2) {
String response = DigestUtils
.md5Hex(ha1 + ":" + nonce + ":" + nonceCount + ":" + clientNonce + ":" + qop + ":" + ha2);
return response;
}
}
Output
401
{Cache-Control=[private], Server=[Microsoft-IIS/10.0], WWW-Authenticate=[Digest realm="CREA.Distribution", nonce="NjM3ODQ5NTI2NzgzNjk6OTRmMjM0NDJlMmVkZGY0MDI1YmE0MzkxNTM5NDhhNmY=", qop="auth"], X-AspNet-Version=[4.0.30319], Set-Cookie=[ARRAffinity=eb0215f43a7fdf079429ba39da6cd0de66afa6b4085fd1016024d539de9de1b3;Path=/;HttpOnly;Domain=data.crea.ca, ASP.NET_SessionId=y5ylejdp2f5q3siitg410lk5; path=/; HttpOnly; SameSite=Lax], Content-Length=[0], Date=[Thu, 07 Apr 2022 18:24:38 GMT], X-Powered-By=[ASP.NET], Content-Type=[application/xml; charset=utf-8]}
WWW-Authenticate: [Digest realm="CREA.Distribution", nonce="NjM3ODQ5NTI2NzgzNjk6OTRmMjM0NDJlMmVkZGY0MDI1YmE0MzkxNTM5NDhhNmY=", qop="auth"]
Digest Header: Digest username="CXLHfDVrziCfvwgCuL8nUahC", realm="CREA.Distribution", nonce="NjM3ODQ5NTI2NzgzNjk6OTRmMjM0NDJlMmVkZGY0MDI1YmE0MzkxNTM5NDhhNmY=", uri="/Login.svc/Login", qop=auth, nc=678, cnonce="afdjas0", response="f7d2445a27173c5e876e3b2833369e64", opaque=""
Complete Response:
GET http://data.crea.ca/Login.svc/Login returned a response status of 200 OK
Response header: {RETS-Request-ID=, Cache-Control=private, Server=Microsoft-IIS/10.0, X-AspNet-Version=4.0.30319, Set-Cookie=ARRAffinity=eb0215f43a7fdf079429ba39da6cd0de66afa6b4085fd1016024d539de9de1b3;Path=/;HttpOnly;Domain=data.crea.ca, RETS-Version=RETS/1.7.2, Content-Length=591, Date=Thu, 07 Apr 2022 18:24:38 GMT, X-Powered-By=ASP.NET, Content-Type=text/xml}
Cookie: X-SESSIONID=95b7bcc5-6d47-4494-936b-5bfaf3485679;Version=1
Getting metadata..
SessionID: X-SESSIONID=95b7bcc5-6d47-4494-936b-5bfaf3485679
Digest Header: Digest username="CXLHfDVrziCfvwgCuL8nUahC", realm="CREA.Distribution", nonce="NjM3ODQ5NTI2NzgzNjk6OTRmMjM0NDJlMmVkZGY0MDI1YmE0MzkxNTM5NDhhNmY=", uri="/Login.svc/Login", qop=auth, nc=678, cnonce="afdjas0", response="f7d2445a27173c5e876e3b2833369e64", opaque=""
MetaDataError: 401
Response of metadata=
public String getPolicy() throws Exception {
String policy_document = "{\"expiration\": \"2020-01-01T00:00:00Z\",\n" +
" \"conditions\": [ \n" +
" {\"bucket\": \"bucket\"}, \n" +
" [\"starts-with\", \"$Content-Type\", \"image/\"],\n" +
" [\"content-length-range\", 0, 100]\n" +
" ]\n" +
"}";
String aws_secret_key = "xxxxx";
String policy = (new BASE64Encoder()).encode(policy_document.getBytes("UTF-8"))
.replaceAll("\n", "").replaceAll("\r", "");
Mac hmac = Mac.getInstance("HmacSHA1");
hmac.init(new SecretKeySpec(
aws_secret_key.getBytes("UTF-8"), "HmacSHA1"));
String signature = (new BASE64Encoder()).encode(
hmac.doFinal(policy.getBytes("UTF-8")))
.replaceAll("\n", "");
return policy;
}
While uploading -
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setHeader("policy", getPolicy());
s3Client.putObject(bucket, key, inputStream, objectMetadata);
Can we pass policy header like above to reject s3 putObject requests that violate policy conditions ?
I think it is possible via s3Client.setBucketPolicy but is there a way to set these policies for each put request ?
I've been trying to establish a connection with an API for more than a week now, to no avail. (Magic Card Market's, authentification documentation here and there). I'm supposed to receive a XML file.
I have what MCM call a "widget" access to their API, meaning that I don't have nor need a oauth_token (it's supposed to be an empty string) for the authorization header, and that I'm not supposed to receive nor use an access token/access secret.
The only things I do have are a consumer key (they call it app token sometimes) and a consumer secret.
Here is how I build my Authorization header :
private static String buildOAuthAuthorization(String method, String request)
throws UnsupportedEncodingException, NoSuchAlgorithmException, InvalidKeyException {
String mkmAppToken = APICredentials.appToken;
String mkmAppSecret = APICredentials.appSecret;
String realm = "https://www.mkmapi.eu/ws/v1.1/games";
String oauthVersion = "1.0";
String oauthConsumerKey = mkmAppToken;
String oauthToken = "";
String oauthSignatureMethod = "HMAC-SHA1";
String oauthTimestamp = Long.toString(System.currentTimeMillis() / 1000);
String oauthNonce = Long.toString(System.currentTimeMillis());
String paramString = "oauth_consumer_key=" + oauthConsumerKey
+ "oauth_nonce=" + oauthNonce
+ "oauth_signature_method=" + oauthSignatureMethod
+ "oauth_timestamp=" + oauthTimestamp
+ "oauth_token=" + oauthToken
+ "oauth_version=" + oauthVersion;
String baseString = method + "&" + rawUrlEncode(realm) + "&" + rawUrlEncode(paramString);
String signingKey = rawUrlEncode(mkmAppSecret) + "&";
Mac mac = Mac.getInstance("HMAC-SHA1");
SecretKeySpec secret = new SecretKeySpec(signingKey.getBytes(), mac.getAlgorithm());
mac.init(secret);
byte[] digest = mac.doFinal(baseString.getBytes());
byte[] oauthSignature = Base64.encode(digest, Base64.URL_SAFE);
String authorizationProperty = "OAuth "
+ "realm=\"" + realm + "\", "
+ "oauth_version=\"" + oauthVersion + "\", "
+ "oauth_timestamp=\"" + oauthTimestamp + "\", "
+ "oauth_nonce=\"" + oauthNonce + "\", "
+ "oauth_consumer_key=\"" + oauthConsumerKey + "\", "
+ "oauth_token=\""+ oauthToken + "\", "
+ "oauth_signature_method=\"" + oauthSignatureMethod + "\", "
+ "oauth_signature=\"" + oauthSignature + "\"";
System.out.println(authorizationProperty);
return authorizationProperty;
}
The actual request is in an AsyncTask :
public static class oAuthRequest extends AsyncTask<String, Integer, StringReader> {
private int lastCode;
#Override
protected StringReader doInBackground(String... requestURLs) {
String method = requestURLs[0];
String url = requestURLs[1];
StringReader result = null;
try {
String authProperty = buildOAuthAuthorization(method, url);
HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
connection.addRequestProperty("Authorization:", authProperty);
lastCode = connection.getResponseCode();
System.out.println("RESPONSE CODE 1 " + lastCode);
// Get content
BufferedReader rd = new BufferedReader(new InputStreamReader(lastCode == 200 ? connection.getInputStream() : connection.getErrorStream()));
StringBuilder sb = new StringBuilder();
String line;
while ((line = rd.readLine()) != null) {
sb.append(line);
}
rd.close();
result = new StringReader(sb.toString());
} catch (NoSuchAlgorithmException | InvalidKeyException | IOException e) {
e.printStackTrace();
}
return result;
}
}
It seems like no matter what I change, I'm always getting a 401.
Things I've tried :
oauthSignature as a String using Base64.encodeToString()
Nonce generation using SecureRandom
With and without the empty oauthToken
Another timestamp generation method (can't remember what though)
signing key with and without app token (theorically I need only the consumer secret, but you never know)
Using HttpsURLConnection instead of HttpURLConnection (the URI start in https, so I thought, hey. But no)
At least 2-3 other different implementations (one who was basically a copy/paste of the Java example in the documentation of course -- it still kind of is one now)
(Probably a lot of things I can't even remember)
At this point I'm wondering if maybe the issue comes from my keys, as I've tried to use the Postman app to test requests with the same results.
When trying to void a in-progress document, I received the invalid_request_body error(The request body is missing or improperly formatted. Data at the root level is invalid). Is there something missing on the request body ?
url = baseURL + "/envelopes/" + envelopeId;
body = "";
// re-use connection object for second request...
conn = InitializeRequest(url, "PUT", body, authenticationHeader);
String requestBody = "\r\n\r\n--BOUNDARY\r\n" +
"Accept: application/xml" +
"Content-Type: application/xml\r\n" +
"\r\n" +
body + "\r\n\r\n--BOUNDARY\r\n" +
"status: voided\r\n" +
"voidedReason: Time-out\r\n" +
"\r\n";
String reqBody2 = "\r\n" + "--BOUNDARY--\r\n\r\n";
DataOutputStream dos= new DataOutputStream( conn.getOutputStream() );
dos.writeBytes(requestBody.toString());
//dos.write(bytes);
dos.writeBytes(reqBody2.toString());
dos.flush();
dos.close();
System.out.println("STEP 2: Retrieving envelope information for envelope " + envelopeId + ".\n");
status = conn.getResponseCode(); // triggers the request
if( status != 200 ) { // 200 = OK
System.out.println(conn);
System.out.println(status);
errorParse(conn, status);
throw new RuntimeException();
}
// display results
response = getResponseBody(conn);
Following up with your comments for the benefit of the community...
This is the request body that worked for you in the end:
request body## String requestBody = "<envelope>" + "<status>voided</status>" + "<voidedReason>user aborted</voidedReason>" + "</envelope>"
So I'm writing a small app to dump a directory of images into the user's tumblr blog, using their provided API: http://www.tumblr.com/docs/en/api
I've gotten plaintext posting to work, but now I need to find out how to send an image file in the POST instead of UTF-8 encoded text, and I'm lost. My code at the moment is returning a 403 forbidden error, as if the username and password were incorrect (they're not), and everything else I try gives me a bad request error. I'd rather not have to use external libraries for this if I can. This is my ImagePost class:
public class ImagePost {
String data = null;
String enc = "UTF-8";
String type;
File img;
public ImagePost(String imgPath, String caption, String tags) throws IOException {
//Construct data
type = "photo";
img = new File(imgPath);
data = URLEncoder.encode("email", enc) + "=" + URLEncoder.encode(Main.getEmail(), enc);
data += "&" + URLEncoder.encode("password", enc) + "=" + URLEncoder.encode(Main.getPassword(), enc);
data += "&" + URLEncoder.encode("type", enc) + "=" + URLEncoder.encode(type, enc);
data += "&" + URLEncoder.encode("data", enc) + "=" + img;
data += "&" + URLEncoder.encode("caption", enc) + "=" + URLEncoder.encode(caption, enc);
data += "&" + URLEncoder.encode("generator", "UTF-8") + "=" + URLEncoder.encode(Main.getVersion(), "UTF-8");
data += "&" + URLEncoder.encode("tags", "UTF-8") + "=" + URLEncoder.encode(tags, "UTF-8");
}
public void send() throws IOException {
// Set up connection
URL tumblrWrite = new URL("http://www.tumblr.com/api/write");
HttpURLConnection http = (HttpURLConnection) tumblrWrite.openConnection();
http.setDoOutput(true);
http.setRequestMethod("POST");
http.setRequestProperty("Content-Type", "image/png");
DataOutputStream dout = new DataOutputStream(http.getOutputStream());
//OutputStreamWriter out = new OutputStreamWriter(http.getOutputStream());
// Send data
http.connect();
dout.writeBytes(data);
//out.write(data);
dout.flush();
System.out.println(http.getResponseCode());
System.out.println(http.getResponseMessage());
dout.close();
}
}
I suggest you use MultipartRequestEntity (successor of deprecated MultipartPostMethod) of the Apache httpclient package. With MultipartRequestEntity you can send a multipart POST request including a file. An example is below:
public static void postData(String urlString, String filePath) {
log.info("postData");
try {
File f = new File(filePath);
PostMethod postMessage = new PostMethod(urlString);
Part[] parts = {
new StringPart("param_name", "value"),
new FilePart(f.getName(), f)
};
postMessage.setRequestEntity(new MultipartRequestEntity(parts, postMessage.getParams()));
HttpClient client = new HttpClient();
int status = client.executeMethod(postMessage);
} catch (HttpException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}