About :
In this Blog, I will be explaining about how to access Twitter GET Resource API from URLDC by passing the OAuth Access token. This can be used as model to the same for other Resource API secured with OAuth 1.0
OAuth Support in
Twitter:
Twitter uses the OAuth authentication for the external
applications to access its resources. We
need to create an API in
Twitter which the external application will access and
through which the resources will be shared by twitter to the external
application.
Twitter uses the HMAC-SHA1 Signature method to create the
Authorization requests. So we will be needing the following HMACUtil java class to sign our OAuth Request calls
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.TreeMap;
import java.util.regex.Pattern;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;
public class HMACSHAUtil {
private static final String TIMESTAMP = "oauth_timestamp";
private static final String SIGN_METHOD = "oauth_signature_method";
private static final String SIGNATURE = "oauth_signature";
private static final String CONSUMER_KEY = "oauth_consumer_key";
private static final String VERSION = "oauth_version";
private static final String NONCE = "oauth_nonce";
private static final String TOKEN = "oauth_token";
private static final String VERIFIER = "oauth_verifier";
private static String CHARSET = "UTF-8";
private static Timer timer = new Timer();
private static final String EMPTY_STRING = "";
private static final Map ENCODING_RULES;
private static final String AMPERSAND_SEPARATED_STRING = "%s&%s&%s";
private static final String HMAC_SHA1 = "HmacSHA1";
private static final String CARRIAGE_RETURN = "\r\n";
static {
Map rules = new HashMap();
rules.put("*", "%2A");
rules.put("+", "%20");
rules.put("%7E", "~");
ENCODING_RULES = Collections.unmodifiableMap(rules);
}
public HMACSHAUtil() {
super();
}
public static String OAUTH1Header(String oauth_token, String consumer_key, String httpMethod,
String url, String consumerSecret, String oauth_token_secret,String verifier) throws Exception {
Map oauthParameters = new TreeMap();
oauthParameters.put(TOKEN, oauth_token);
oauthParameters.put(TIMESTAMP, getTimestampInSeconds());
oauthParameters.put(NONCE, getNonce());
oauthParameters.put(CONSUMER_KEY, consumer_key);
oauthParameters.put(SIGN_METHOD, "HMAC-SHA1");
oauthParameters.put(VERSION, "1.0");/**/
if(verifier!=null && !verifier.isEmpty())
{
oauthParameters.put(VERIFIER, verifier);
}
oauthParameters.put(VERSION, "1.0");
String params = encode(asFormUrlEncodedString(oauthParameters));
String baseString =
String.format(AMPERSAND_SEPARATED_STRING, encode(httpMethod), encode(getSanitizedUrl(url)), params);
String signature = doSign(baseString, encode(consumerSecret) + '&' + encode(oauth_token_secret));
oauthParameters.put(SIGNATURE, signature);
return generateOAuth1Header(oauthParameters);
}
private static String generateOAuth1Header(Map parameters) {
StringBuffer header = new StringBuffer(parameters.size() * 20);
header.append("OAuth ");
for (String key : parameters.keySet()) {
if (header.length() > "OAuth ".length()) {
header.append(", ");
}
header.append(String.format("%s=\"%s\"", key, encode(parameters.get(key))));
}
return header.toString();
}
private static String doSign(String toSign, String keyString) throws Exception {
SecretKeySpec key = new SecretKeySpec((keyString).getBytes(CHARSET), HMAC_SHA1);
Mac mac = Mac.getInstance(HMAC_SHA1);
mac.init(key);
byte[] bytes = mac.doFinal(toSign.getBytes(CHARSET));
return new String(Base64.encodeBase64(bytes)).replace(CARRIAGE_RETURN, EMPTY_STRING);
}
static class Timer {
Long getMilis() {
return System.currentTimeMillis();
}
Integer getRandomInteger() {
return new Random().nextInt();
}
}
private static String getNonce() {
Long ts = getTs();
return String.valueOf(ts + timer.getRandomInteger());
}
private static String getTimestampInSeconds() {
return String.valueOf(getTs());
}
private static String getSanitizedUrl(String url) {
return url.replaceAll("\\?.*", "").replace("\\:\\d{4}", "");
}
private static Long getTs() {
return timer.getMilis() / 1000;
}
private static String asFormUrlEncodedString(Map oauthParameters) {
if (oauthParameters.size() == 0)
return EMPTY_STRING;
StringBuilder builder = new StringBuilder();
for (String p : oauthParameters.keySet()) {
builder.append('&').append(encode(p).concat("=").concat(encode(oauthParameters.get(p))));
}
return builder.toString().substring(1);
}
private static String applyRule(String encoded, String toReplace, String replacement) {
return encoded.replaceAll(Pattern.quote(toReplace), replacement);
}
private static String encode(String plain) {
String encoded = "";
try {
encoded = URLEncoder.encode(plain, CHARSET);
} catch (UnsupportedEncodingException uee) {
throw new RuntimeException("Charset not found while encoding string: " + CHARSET, uee);
}
for (Map.Entry rule : ENCODING_RULES.entrySet()) {
encoded = applyRule(encoded, rule.getKey(), rule.getValue());
}
return encoded;
}
}
Pre requisites:
1. Obtain the Consumer key and Secret for the application
created in twitter
2. The HMACSHA utility java class was used to created the
encoded authorized value from the parameters provided.
3. Any Jdev Versions with Http Header Parameter support in URL DataControl
Steps in JDeveloper :
1. Create a jspx page e.g index page with backing bean, in the backing bean
create a method in which URLConnection is used to access the
http://api.twitter.com/oauth/request_token
and the authorization parameters are set to the URL before connecting. HMACSHAUtil.OAUTH1Header
method is used to create the Authorization value using the Consumer key and
Consumer Secret obtained while creating the application in Twitter .
Code Snippet of backing bean method :
String requestToken = HMACSHAUtil.OAUTH1Header("",this.getConsumer_key(),"POST","http://api.twitter.com/oauth/request_token", this.getConsumer_secret(),"","");
URL url = new URL( "http://api.twitter.com/oauth/request_token" );
URLConnection myURLConnection = url.openConnection();
HttpURLConnection conn = (HttpURLConnection)myURLConnection;
conn.setRequestMethod("POST");
conn.setRequestProperty("Authorization", requestToken);
conn.connect();
2. Once the Connection is successful read the content of the response. Twitter sends the response stream in two different ways randomly; they are normal UTF-8 Input Stream and encoded GZIPInput Stream so ensure that your code handles both the types of response
3. Read the Temporary oauth token and oauth secret value from the response streams and set them to the session scope as we will need them later.
4. Now navigate to the following URL https://api.twitter.com/oauth/authorize?oauth_token=”
Value obtained in the previous step”.
5. The User will be prompted with a login screen to provide their twitter username and password to authorize the application to read their tweets.
6. Once the Authorization is successful, twitter redirects the request to the call back URL provided in the API, and along with it sends the oauth verifier as a parameter.
7. In the jspx e.g redirect page to which twitter redirects, retrieve the oauth verifier from the URL and set in an bean.
8. In the same page using the Client and Server listener, call a method in the backing bean in which the URL connection is again used to connect to the following
URL : http://api.twitter.com/oauth/access_token with the appropriate authorization parameters to get the permanent access token as shown below.
Code Snippet
String requestToken = HMACSHAUtil.OAUTH1Header(oauh_token,consumKey,"POST","http://api.twitter.com/oauth/access_token",consumSec,oauth_secret,oauth_verifier);
URL url = new URL( "http://api.twitter.com/oauth/access_token" );
myURLConnection = url.openConnection();
conn = (HttpURLConnection)myURLConnection;
conn.setRequestMethod("POST");
conn.setRequestProperty("Authorization", requestToken);
conn.connect();
Here consumSec is the consumer secret obtained after registering the application in Twitter.
9. Read the response stream the same way as mentioned in the step 2, now from the response stream get the permanent oauth token and permanent oauth secret.
10. Using the utility class create an Authorization parameter which will be used to fetch the tweets from twitter as shown in the code snippet below.
Code Snippet
String HeaderToken = HMACSHAUtil.OAUTH1Header(perm_oauth_token,consumkey,"GET","http://api.twitter.com/1/statuses/friends_timeline.xml",consumSec,perm_oauth_secret,null);
The URL which will be used to fetch the tweets and along with the operation type (GET) is passed as parameters to the Utility method so that this authorization parameter allows twitter to recognize the user requesting for the appropriate operation.
11.The above steps have to be executed only once to obtain the permanent authorization parameters. Set the HeaderToken(Authorization parameter) value in the Session Scope.
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.getExternalContext().getSessionMap().put("FinalHeaderToken",HeaderToken);
12. Now create a new jspx page e.g viewtweets.jspx with a backing bean associated to it.
13. Create URLDC with GET operation type with a dummy url where ensure you select the Include Http Header option and provide the following
xsd
14. After creating the DC, Open the DataControl.dcx file and edit the attribute
Source="/1/statuses/friends_timeline.xml" and in Connections.xml update the attribute
url="http://api.twitter.com" of urlconnection field.
15. In the backing bean of the viewtweets.jspx put a Map variable and generate accessors as shown below
private Map httpHeaders = new HashMap();
public void setHttpHeaders(Map httpHeaders) {
this.httpHeaders = httpHeaders;
}
public Map getHttpHeaders() {
return httpHeaders;
}
16. In the same backing bean, inside the method setF1 which gets called first when the page is rendering as shown below
public void setF1(RichForm f1) {
this.f1 = f1;
String HeaderToken = (String)FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("FinalHeaderToken");
System.out.println("Session Header Token:"+HeaderToken);
httpHeaders.put("Authorization",HeaderToken);
}
17. Drag and Drop the status from the return type of the method in the DC palette onto the page as ADF Form
18. In the Edit Action Binding,set the map as the value for the Header parameter exposed.
Running the Application :
Before running the application we have to ensure that the HostNameVerifier is set to NONE in the WLS.
When Run the Index page and click on the Button the following process takes place which is shown as pictorial representation below