Java tutorial
/** * $Id$ * * neodym * A java library to access the REST API of amun * * Copyright (c) 2011 Christoph Kappestein <k42b3.x@gmail.com> * * This file is part of neodym. neodym is free software: you can * redistribute it and/or modify it under the terms of the GNU * General Public License as published by the Free Software Foundation, * either version 3 of the License, or at any later version. * * neodym is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with neodym. If not, see <http://www.gnu.org/licenses/>. */ package com.k42b3.neodym.oauth; import java.awt.Desktop; import java.net.URI; import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Random; import java.util.Set; import java.util.logging.Logger; import javax.swing.JOptionPane; import org.apache.commons.codec.digest.DigestUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.params.BasicHttpParams; import org.apache.http.params.HttpConnectionParams; import org.apache.http.params.HttpParams; import org.apache.http.util.EntityUtils; import com.k42b3.neodym.Http; import com.k42b3.neodym.TrafficItem; import com.k42b3.neodym.TrafficListenerInterface; /** * Oauth * * @author Christoph Kappestein <k42b3.x@gmail.com> * @license http://www.gnu.org/licenses/gpl.html GPLv3 * @link http://code.google.com/p/delta-quadrant * @version $Revision$ */ public class Oauth { private Http http; private OauthProvider provider; private String token; private String tokenSecret; private boolean callbackConfirmed; private String verificationCode; private boolean authed = false; private TrafficListenerInterface trafficListener; private Logger logger = Logger.getLogger("com.k42b3.neodym"); public Oauth(Http http, OauthProvider provider) { this.http = http; this.provider = provider; if ((provider.getToken() != null && !provider.getToken().isEmpty()) && (provider.getTokenSecret() != null && !provider.getTokenSecret().isEmpty())) { this.auth(provider.getToken(), provider.getTokenSecret()); } } public void auth(String token, String tokenSecret) { this.setToken(token); this.setTokenSecret(tokenSecret); this.authed = true; } public void setToken(String token) { this.token = token; } public void setTokenSecret(String tokenSecret) { this.tokenSecret = tokenSecret; } public String getToken() { return token; } public String getTokenSecret() { return tokenSecret; } public boolean isAuthed() { return authed; } public boolean requestToken() throws Exception { // add values HashMap<String, String> values = new HashMap<String, String>(); String requestMethod = "GET"; values.put("oauth_consumer_key", this.provider.getConsumerKey()); values.put("oauth_signature_method", provider.getMethod()); values.put("oauth_timestamp", this.getTimestamp()); values.put("oauth_nonce", this.getNonce()); values.put("oauth_version", this.getVersion()); values.put("oauth_callback", "oob"); // add get vars to values URL requestUrl = new URL(provider.getRequestUrl()); values.putAll(parseQuery(requestUrl.getQuery())); // build base string String baseString = this.buildBaseString(requestMethod, provider.getRequestUrl(), values); // get signature SignatureInterface signature = this.getSignature(); if (signature == null) { throw new Exception("Invalid signature method"); } // build signature values.put("oauth_signature", signature.build(baseString, provider.getConsumerSecret(), "")); // add header to request HashMap<String, String> header = new HashMap<String, String>(); header.put("Authorization", "OAuth realm=\"neodym\", " + this.buildAuthString(values)); String responseContent = http.request(Http.GET, provider.getRequestUrl(), header); // parse response this.token = null; this.tokenSecret = null; this.callbackConfirmed = false; HashMap<String, String> response = parseQuery(responseContent); Set<String> keys = response.keySet(); for (String key : keys) { if (key.equals("oauth_token")) { this.token = response.get(key); logger.info("Received token: " + this.token); } if (key.equals("oauth_token_secret")) { this.tokenSecret = response.get(key); logger.info("Received token secret: " + this.tokenSecret); } if (key.equals("oauth_callback_confirmed")) { this.tokenSecret = response.get(key); this.callbackConfirmed = response.get(key).equals("1"); } } if (this.token == null) { throw new Exception("No oauth token received"); } if (this.tokenSecret == null) { throw new Exception("No oauth token secret received"); } if (this.callbackConfirmed != true) { throw new Exception("Callback was not confirmed"); } return true; } public boolean authorizeToken() throws Exception { String url; if (this.provider.getAuthorizationUrl().indexOf('?') == -1) { url = this.provider.getAuthorizationUrl() + "?oauth_token=" + this.token; } else { url = this.provider.getAuthorizationUrl() + "&oauth_token=" + this.token; } URI authUrl = new URI(url); if (Desktop.isDesktopSupported()) { Desktop desktop = Desktop.getDesktop(); if (desktop.isSupported(Desktop.Action.BROWSE)) { desktop.browse(authUrl); } else { JOptionPane.showMessageDialog(null, "Visit the following URL: " + authUrl); } } else { JOptionPane.showMessageDialog(null, "Visit the following URL: " + authUrl); } verificationCode = JOptionPane.showInputDialog("Please enter the verification Code"); return true; } public boolean accessToken() throws Exception { // add values HashMap<String, String> values = new HashMap<String, String>(); String requestMethod = "GET"; values.put("oauth_consumer_key", this.provider.getConsumerKey()); values.put("oauth_token", this.token); values.put("oauth_signature_method", provider.getMethod()); values.put("oauth_timestamp", this.getTimestamp()); values.put("oauth_nonce", this.getNonce()); values.put("oauth_verifier", this.verificationCode); // add get vars to values URL accessUrl = new URL(provider.getAccessUrl()); values.putAll(parseQuery(accessUrl.getQuery())); // build base string String baseString = this.buildBaseString(requestMethod, provider.getAccessUrl(), values); // get signature SignatureInterface signature = this.getSignature(); if (signature == null) { throw new Exception("Invalid signature method"); } // build signature values.put("oauth_signature", signature.build(baseString, provider.getConsumerSecret(), this.tokenSecret)); // add header to request HashMap<String, String> header = new HashMap<String, String>(); header.put("Authorization", "OAuth realm=\"neodym\", " + this.buildAuthString(values)); String responseContent = http.request(Http.GET, provider.getAccessUrl(), header); // parse response this.token = null; this.tokenSecret = null; HashMap<String, String> response = parseQuery(responseContent); Set<String> keys = response.keySet(); for (String key : keys) { if (key.equals("oauth_token")) { this.token = response.get(key); logger.info("Received token: " + this.token); } if (key.equals("oauth_token_secret")) { this.tokenSecret = response.get(key); logger.info("Received token secret: " + this.tokenSecret); } } if (this.token == null) { throw new Exception("No oauth token received"); } if (this.tokenSecret == null) { throw new Exception("No oauth token secret received"); } return true; } @SuppressWarnings("unchecked") public void signRequest(HttpRequestBase request) throws Exception { // add values HashMap<String, String> values = new HashMap<String, String>(); HashMap<String, String> auth; values.put("oauth_consumer_key", this.provider.getConsumerKey()); values.put("oauth_token", this.token); values.put("oauth_signature_method", provider.getMethod()); values.put("oauth_timestamp", this.getTimestamp()); values.put("oauth_nonce", this.getNonce()); auth = (HashMap<String, String>) values.clone(); // add get vars to values values.putAll(parseQuery(request.getURI().getQuery())); // build base string String baseString = this.buildBaseString(request.getMethod(), request.getURI().toString(), values); // get signature SignatureInterface signature = this.getSignature(); if (signature == null) { throw new Exception("Invalid signature method"); } // build signature auth.put("oauth_signature", signature.build(baseString, provider.getConsumerSecret(), this.tokenSecret)); // add header to request request.addHeader("Authorization", "OAuth realm=\"neodym\", " + this.buildAuthString(auth)); } private String buildAuthString(HashMap<String, String> values) { StringBuilder authString = new StringBuilder(); Iterator<Entry<String, String>> it = values.entrySet().iterator(); while (it.hasNext()) { Entry<String, String> e = it.next(); authString.append(urlEncode(e.getKey()) + "=\"" + urlEncode(e.getValue()) + "\", "); } String str = authString.toString(); // remove ", " from string str = str.substring(0, str.length() - 2); return str; } private String buildBaseString(String requestMethod, String url, HashMap<String, String> params) throws Exception { StringBuilder base = new StringBuilder(); base.append(urlEncode(this.getNormalizedMethod(requestMethod))); base.append('&'); base.append(urlEncode(this.getNormalizedUrl(url))); base.append('&'); base.append(urlEncode(this.getNormalizedParameters(params))); logger.fine("BaseString: " + base.toString()); return base.toString(); } private String getNormalizedParameters(HashMap<String, String> params) { Iterator<Entry<String, String>> it = params.entrySet().iterator(); List<String> keys = new ArrayList<String>(); while (it.hasNext()) { Entry<String, String> e = it.next(); keys.add(e.getKey()); } // sort params Collections.sort(keys); // build normalized params StringBuilder normalizedParams = new StringBuilder(); for (int i = 0; i < keys.size(); i++) { normalizedParams.append(urlEncode(keys.get(i)) + "=" + urlEncode(params.get(keys.get(i))) + "&"); } String str = normalizedParams.toString(); // remove trailing & str = str.substring(0, str.length() - 1); return str; } private String getNormalizedUrl(String rawUrl) throws Exception { rawUrl = rawUrl.toLowerCase(); URL url = new URL(rawUrl); int port = url.getPort(); if (port == -1 || port == 80 || port == 443) { return url.getProtocol() + "://" + url.getHost() + url.getPath(); } else { return url.getProtocol() + "://" + url.getHost() + ":" + port + url.getPath(); } } private String getNormalizedMethod(String method) { return method.toUpperCase(); } private String getTimestamp() { return "" + (System.currentTimeMillis() / 1000); } private String getNonce() { try { byte[] nonce = new byte[32]; Random rand; rand = SecureRandom.getInstance("SHA1PRNG"); rand.nextBytes(nonce); return DigestUtils.md5Hex(rand.toString()); } catch (Exception e) { return DigestUtils.md5Hex("" + System.currentTimeMillis()); } } private String getVersion() { return "1.0"; } @SuppressWarnings("unused") private HttpEntity httpRequest(String method, String url, Map<String, String> header, String body) throws Exception { // build request HttpParams httpParams = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParams, 6000); DefaultHttpClient httpClient = new DefaultHttpClient(httpParams); HttpRequestBase request; if (method.equals("GET")) { request = new HttpGet(url); } else if (method.equals("POST")) { MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); entity.addPart("text", new StringBody(body)); request = new HttpPost(url); ((HttpPost) request).setEntity(entity); } else { throw new Exception("Invalid request method"); } // header Set<String> keys = header.keySet(); for (String key : keys) { request.setHeader(key, header.get(key)); } // execute HTTP Get Request logger.info("Request: " + request.getRequestLine()); HttpResponse httpResponse = httpClient.execute(request); HttpEntity entity = httpResponse.getEntity(); String responseContent = EntityUtils.toString(entity); // log traffic if (trafficListener != null) { TrafficItem trafficItem = new TrafficItem(); trafficItem.setRequest(request); trafficItem.setResponse(httpResponse); trafficItem.setResponseContent(responseContent); trafficListener.handleRequest(trafficItem); } // check status code int statusCode = httpResponse.getStatusLine().getStatusCode(); if (!(statusCode >= 200 && statusCode < 300)) { JOptionPane.showMessageDialog(null, responseContent); throw new Exception("No successful status code"); } return entity; } private SignatureInterface getSignature() throws Exception { String cls; if (provider.getMethod().equals("HMAC-SHA1")) { cls = "com.k42b3.neodym.oauth.HMACSHA1"; } else if (provider.getMethod().equals("PLAINTEXT")) { cls = "com.k42b3.neodym.oauth.PLAINTEXT"; } else { throw new Exception("Invalid signature method"); } return (SignatureInterface) Class.forName(cls).newInstance(); } public static String urlEncode(String content) { try { if (!content.isEmpty()) { String encoded = URLEncoder.encode(content, "UTF8"); encoded = encoded.replaceAll("%7E", "~"); return encoded; } else { return ""; } } catch (Exception e) { return ""; } } public static HashMap<String, String> parseQuery(String query) throws Exception { HashMap<String, String> map = new HashMap<String, String>(); if (query != null) { String[] params = query.split("&"); for (int i = 0; i < params.length; i++) { String[] pair = params[i].split("="); if (pair.length >= 1) { String name = URLDecoder.decode(pair[0], "UTF-8"); String value = pair.length == 2 ? URLDecoder.decode(pair[1], "UTF-8") : ""; map.put(name, value); } } } return map; } }