【Twitter】JavaでのXauth AccessToken取得サンプル

P3に組み込むべく、作ってみた。オリジナルはid:tototoshi「request tokenを取得するJavaプログラム」
実際にはここから更に手を加えることになると思うのだけど、とりあえずご参考ということで。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.UUID;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;


public class AccessToken {
	private static final String CONSUMER_KEY = "xxxxxxxxxxxxxx";
	private static final String CONSUMER_SECRET = "xxxxxxxxxxxxxxxxxxxxxx";

	private static final String SIGNATURE_METHOD = "HMAC-SHA1";
	private static final String OAUTH_VERSION = "1.0";
	private static final String ALGOTITHM = "HmacSHA1";
	private Map<String, String> parameterMap;
	private SortedMap<String, String> oauthParametersMap;
	private boolean useProxy = false;
	private String method = null;
	private String api = null;
	private String oauthToken;
	private String oauthTokenSecret;

	public AccessToken(String method, String api, Map<String, String> parameterMap) {
		this.method = method;
		this.api = api;
		this.parameterMap = parameterMap;
	}

	public String get(){
		return oauthToken;
	}

	public String getSecret(){
		return oauthTokenSecret;
	}

	public static void main(String[] args) {
		Map<String, String> map = new HashMap<String, String>();
		try {
			map.put("x_auth_mode", SignatureEncode.encode"client_auth"));
			map.put("x_auth_password", SignatureEncode.encode"パスワード"));
			map.put("x_auth_username", SignatureEncode.encode"スクリーンネーム"));
		} catch (UnsupportedEncodingException ignore) {
		}
		AccessToken accessToken = new AccessToken(
				"POST",
				"https://api.twitter.com/oauth/access_token",
				map);
		accessToken.request();
		System.out.println(accessToken.get());
		System.out.println(accessToken.getSecret());
	}

	public void request() {
		setProperty();
		oauthParametersMap = createParametersMap();
		HttpURLConnection urlconn = null;
		BufferedReader reader = null;
		try {
			String apiParamter = createParameters();
			URL url = new URL(api +
								(apiParamter.length() > 0 ? "?" + apiParamter : ""));
			urlconn = (HttpURLConnection) url.openConnection();
			urlconn.setRequestMethod(method);
			urlconn.addRequestProperty("Authorization", createAuthorizationValue());
			urlconn.connect();
	    	if (urlconn.getResponseCode() == HttpURLConnection.HTTP_OK) {
	    		reader = new BufferedReader(new InputStreamReader(urlconn.getInputStream()));
				String line = reader.readLine();
				System.out.println(line);
				String[] parameters = line.split("&");
				for (String parameter : parameters) {
					String[] keyAndValue = parameter.split("=");
					if (keyAndValue.length < 2) {
						continue;
					}
					String key = keyAndValue[0];
					String value = keyAndValue[1];
					if("oauth_token".equals(key)){
						oauthToken = value;
					}else if("oauth_token_secret".equals(key)){
						oauthTokenSecret = value;
					}
				}
	    	} else {
	    		urlconn.disconnect();
	    	}
		} catch (IOException e) {
			// "Read Time out";
			e.printStackTrace();
		} catch (InvalidKeyException ignore) {
		} catch (NoSuchAlgorithmException ignore) {
		}finally{
			if (reader != null) {
				try {
					reader.close();
				} catch (IOException ignore) {
				}
			}
			if (urlconn != null) {
				urlconn.disconnect();
			}
		}
	}

	private void setProperty() {
	    System.setProperty("sun.net.client.defaultReadTimeout", "15000");
	    System.setProperty("sun.net.client.defaultConnectTimeout", "15000");
	    if(useProxy){
	    	System.setProperty("http.proxyHost", "proxyServerName");
	    	System.setProperty("http.proxyPort", "proxyPortNo");
	    }else{
	    	System.setProperty("http.nonProxyHosts", "localhost");
	    }
	}

	private SortedMap<String, String> createParametersMap() {
		SortedMap<String, String> map = new TreeMap<String, String>();
		map.put("oauth_consumer_key", CONSUMER_KEY);
		map.put("oauth_nonce", UUID.randomUUID().toString());
		map.put("oauth_signature_method", SIGNATURE_METHOD);
		map.put("oauth_timestamp", getTimeStamp());
		map.put("oauth_version", OAUTH_VERSION);
		return map;
	}

	private String createParameters() {
		if (parameterMap == null || parameterMap.size() == 0) {
			return "";
		}
		StringBuilder builder = new StringBuilder();
		for (Map.Entry<String, String> param : parameterMap.entrySet()) {
			builder.append(param.getKey() + "=");
			builder.append(param.getValue());
			builder.append("&");
		}
		return builder.toString().substring(0, builder.length() -1);
	}

	private String createAuthorizationValue() throws InvalidKeyException, NoSuchAlgorithmException, UnsupportedEncodingException {
		StringBuilder builder = new StringBuilder();
		builder.append("OAuth ");
		for (Map.Entry<String, String> param : oauthParametersMap.entrySet()) {
			builder.append(param.getKey() + "=");
			builder.append("\"" + param.getValue() + "\",");
		}
		builder.append("oauth_signature" + "=");
		builder.append("\"" +
						getSignature(getSignatureBaseString(), getKey())
						+ "\"");
		return builder.toString();
	}

	private String getKey() {
		StringBuilder builder = new StringBuilder();
		builder.append(CONSUMER_SECRET);
		builder.append("&");
		return builder.toString();
	}

	private String getRequestParameters() {
		if (parameterMap != null && parameterMap.size() > 0) {
			for (Map.Entry<String, String> param : parameterMap.entrySet()) {
				oauthParametersMap.put(param.getKey(), param.getValue());
			}
		}
		StringBuilder builder = new StringBuilder();
		for (Map.Entry<String, String> param : oauthParametersMap.entrySet()) {
			builder.append(param.getKey());
			builder.append("=");
			builder.append(param.getValue());
			builder.append("&");
		}
		return builder.toString().substring(0, builder.length() -1);
	}

	private String getSignatureBaseString() throws UnsupportedEncodingException {
		return method + "&" + encodeURL(api) + "&" + SignatureEncode.encode(getRequestParameters());
	}

	private String getSignature(String signatureBaseString, String keyString)
		throws NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException{
		Mac mac = Mac.getInstance(ALGOTITHM);
		Key key= new SecretKeySpec(keyString.getBytes(), ALGOTITHM);
		mac.init(key);
		byte[] digest = mac.doFinal(signatureBaseString.getBytes());
		return encodeURL(Base64.encodeBytes(digest));
	}

	private String encodeURL(String str) {
		String encord = null;
		try {
			encord = URLEncoder.encode(str, "UTF-8");
		} catch (UnsupportedEncodingException ignore) {
		}
		return encord;
	}

	private String getTimeStamp() {
		return Long.toString(System.currentTimeMillis() / 1000);
	}
}
import java.io.UnsupportedEncodingException;

public class SignatureEncode {
	private static final String UNRESERVEDCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";

	public static final String encode(String s) throws UnsupportedEncodingException {
        byte[] bytes = s.getBytes("utf-8");
        StringBuffer builder = new StringBuffer();
        for (byte b: bytes){
        	char c = (char) b;
        	if (UNRESERVEDCHARS.indexOf(String.valueOf(c)) >= 0) {
        		builder.append(String.valueOf(c));
        	} else {
        		builder.append("%" +
        				String.valueOf(Integer.toHexString(b > 0 ? b : b + 256)).toUpperCase());
        	}
        }
        return builder.toString();
	}
}

※:Base64にはこちらのモノを使用した。

id:tototoshiと@aki_null,@kiri_featherに千の感謝を。
あと、おかしな所とか、気づかれたことがありましたらご指摘ください。

【追記】
プロキシ環境下だとOauthが通らない、という話があるけど、これならどうだろうか…