
OAuthサンプルによって解説しようと思っていましたが、
結局暗号化を行って、テスト書いて、色々分かりやすくコードをいじってってこの状態になりました。
以下のコードでアクセスできます。
- package jp.co.ziro.evernote.controller;
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.UnsupportedEncodingException;
- import java.net.HttpURLConnection;
- import java.net.MalformedURLException;
- import java.net.URL;
- import java.net.URLDecoder;
- import java.net.URLEncoder;
- import java.security.InvalidKeyException;
- import java.security.NoSuchAlgorithmException;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.Random;
- import java.util.TreeMap;
- import java.util.logging.Logger;
- import javax.crypto.Mac;
- import javax.crypto.spec.SecretKeySpec;
- import org.apache.commons.codec.binary.Base64;
- import org.slim3.util.ApplicationMessage;
- /**
- * <pre>
- * EvernoteへのOAuthアクセスクラス
- * - 認証URL作成 setCallbackUrl()
- * - アクセストークン取得時はsendRequest(tokenSecret)
- * で処理を行います。
- *
- * 認証Urlの作成
- * 1.コンストラクタにより、固定値を設定する
- * 2.使用側は setCallbackUrl() にコールバックするUrlを設定する
- * 3.createAuthorization() の戻り値が認証Urlを取得
- * 4.getTokenSecret(),secretを取得(保存)
- *
- * 認証Urlにアクセスする
- * 認可画面が表示され、そこでの結果がコールバックに渡される
- *
- * コールバックUrlで行う事
- * 1.コンストラクタにより固定値を設定する
- * 2.oauth_verifier,oauth_tokenがコールバックに呼ばれるのでそれを
- * setToken() setVerifier() で設定
- * verifierがnullだったら、認証が行われていない。
- * 3.sendRequest()を行う。引数は認証Url時に作成したsecret
- * 4.oauth_token にリクエストトークン、edam_shard にAPIアクセス用のIdが渡されます。
- *
- * これらでthriftのAPIを利用して、Evernoteにアクセスできます。
- *
- * </pre>
- * @author secondarykey
- */
- public class EvernoteRequest {
- @SuppressWarnings("unused")
- private static Logger logger = Logger.getLogger(EvernoteRequest.class.getName());
- /**
- * EverNoteのAPI ConsumerKey
- */
- private static final String CONSUMERKEY = ApplicationMessage
- .get("evernote.api.key");
- /**
- * EvernoteのAPI ConsumerSecret
- */
- private static final String CONSUMERSECRET = ApplicationMessage
- .get("evernote.api.secret");
- /**
- * アクセスするUrl
- */
- private static final String BASEURL = ApplicationMessage
- .get("evernote.api.baseUrl");
- /**
- * リクエストToken取得用のUrl
- */
- private static final String requestUrl = BASEURL
- + ApplicationMessage.get("evernote.api.requestTokenUrl");
- /**
- * 認証のUrl
- */
- private static final String authorizationUrlBase = BASEURL + ApplicationMessage.get("evernote.api.oauthUrl");
- /**
- * 文字コード
- */
- private static final String CHARSET = "UTF-8";
- /**
- * キー作成用のGETメソッド名
- */
- private static final String REQUEST_METHOD = "GET";
- /**
- * OAuth nonces用のランダム値生成
- */
- private static final Random random = new Random();
- /**
- * ベースのURL
- * @return リクエストするBaseUrl
- */
- public static String getBaseUrl() {
- return BASEURL;
- }
- /**
- * リクエストするパラメーター
- */
- private Map<string, string=""> parameters = new TreeMap<string, string="">();
- /**
- * TokenSecret
- * getTokenSecret()時に空の場合、Exceptionを発生
- */
- private String tokenSecret = null;
- /**
- * <pre>
- * コンストラクタ
- * パラメータ固定値を設定する
- * </pre>
- */
- public EvernoteRequest() {
- setParameter("oauth_consumer_key", CONSUMERKEY);
- setParameter("oauth_timestamp", getTimestamp());
- setParameter("oauth_nonce", Long.toHexString(random.nextLong()));
- setParameter("oauth_version", "1.0");
- //暗号化を行う
- setParameter("oauth_signature_method", "HMAC-SHA1");
- }
- /**
- * タイムスタンプを作成
- * @return 現在時刻の秒をString化した値
- */
- private String getTimestamp() {
- return Long.toString(System.currentTimeMillis() / 1000);
- }
- /**
- * コールバックURLを設定する
- * @param callbackUrl コールバックUrl
- */
- public void setCallbackUrl(String callbackUrl) {
- setParameter("oauth_callback", callbackUrl);
- }
- /**
- * リクエストトークンを設定
- * @param requestToken コールバックされたリクエストトークン
- */
- public void setToken(String requestToken) {
- //それぞれをリクエスターに設定
- setParameter( "oauth_token", requestToken);
- }
- /**
- * Verifierの設定
- * @param verifier コールバックされたverifier値
- */
- public void setVerifier(String verifier) {
- setParameter( "oauth_verifier", verifier);
- }
- /**
- * リクエスト時の引数設定
- * @param name パラメータ名称
- * @param value パラメータの値
- */
- private void setParameter(String name, String value) {
- parameters.put(name, value);
- }
- /**
- * Encodes this request as a single URL that can be opened.
- */
- private String getUrl() {
- return requestUrl + "?" + join();
- }
- /**
- * HTTP引数の連結
- * @return Url引数にして返す
- */
- private String join() {
- StringBuilder sb = new StringBuilder();
- //開始フラグ
- boolean firstParam = true;
- //引数数繰り返す
- for (Map.Entry<string, string=""> parameter : parameters.entrySet()) {
- //最初の処理
- if (firstParam) {
- firstParam = false;
- } else {
- sb.append('&');
- }
- //キー値を取得
- sb.append(parameter.getKey());
- //連結子を設定
- sb.append('=');
- try {
- //値をエンコードして設定
- sb.append(URLEncoder.encode(parameter.getValue(), CHARSET));
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("エンコード時の例外", e);
- }
- }
- return sb.toString();
- }
- /**
- * リクエストを行う
- * @param
- *
- * @throws IOException
- * if a problem occurs making the request or getting the reply.
- */
- public Map<string, string=""> sendRequest(String tokenSecret) {
- //Signatureを設定
- setParameter("oauth_signature", createSignature(tokenSecret));
- //アクセスする
- HttpURLConnection connection;
- try {
- connection = (HttpURLConnection) (new URL(getUrl())).openConnection();
- } catch (MalformedURLException e) {
- throw new RuntimeException("アクセスに対する例外",e);
- } catch (IOException e) {
- throw new RuntimeException("アクセスに対する例外",e);
- }
- int responseCode;
- String responseMessage;
- try {
- responseCode = connection.getResponseCode();
- responseMessage = connection.getResponseMessage();
- } catch (IOException e) {
- throw new RuntimeException("結果取得を行う",e);
- }
- // リクエストが正常じゃない場合
- if (responseCode != HttpURLConnection.HTTP_OK) {
- throw new RuntimeException("Server returned error code: " + responseCode + " " + responseMessage);
- }
- return createResponseMap(connection);
- }
- /**
- * <pre>
- * 暗号化を行う
- * 1. メソッド名&アクセスUrl&HTTPの引数の文字列を作成
- * 2. secretをTokenと連結してsecretをバイト化
- * 3. Secret値を元にSignatureStringを暗号化を行う
- * 4. その値をBase64で16進数かする
- * </pre>
- * @param tokenSecret リクエストToken取得時に発行されたSecret
- * @return 暗号化したSecret値
- */
- private String createSignature(String tokenSecret) {
- String encodeUrl = encode(requestUrl);
- String encodeJoin = encode(join());
- String signatureBaseString = REQUEST_METHOD + "&" + encodeUrl + "&" + encodeJoin;
- // 署名対象のテキストを作成
- String secret = CONSUMERSECRET + "&";
- //TokenSecretが存在する場合
- if (tokenSecret != null) {
- secret += tokenSecret;
- }
- byte[] secretyKeyBytes;
- try {
- secretyKeyBytes = secret.getBytes(CHARSET);
- } catch (UnsupportedEncodingException e2) {
- throw new RuntimeException("エンコード時の失敗",e2);
- }
- String hmac = "HmacSHA1";
- SecretKeySpec secretKeySpec = new SecretKeySpec(secretyKeyBytes,hmac);
- Mac mac;
- try {
- mac = Mac.getInstance(hmac);
- mac.init(secretKeySpec);
- } catch (NoSuchAlgorithmException e1) {
- throw new RuntimeException("暗号化インスタンス取得失敗",e1);
- } catch (InvalidKeyException e1) {
- throw new RuntimeException("暗号化インスタンス取得失敗",e1);
- }
- byte[] data;
- try {
- data = signatureBaseString.getBytes(CHARSET);
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException(CHARSET + " is unsupported!", e);
- }
- byte[] rawHmac = mac.doFinal(data);
- Base64 encoder = new Base64();
- return new String(encoder.encode(rawHmac));
- }
- /**
- * 値のUrlエンコード
- * @param value エンコードする値
- * @return エンコードした値
- */
- private String encode(String value) {
- String rtnData;
- try {
- rtnData = URLEncoder.encode(value,CHARSET);
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("エンコード時の例外",e);
- }
- return rtnData;
- }
- /**
- * レスポンスの値を取得
- *
- * @param connection
- * @return
- */
- private Map<string, string=""> createResponseMap(HttpURLConnection connection) {
- BufferedReader bufferedReader;
- String result;
- try {
- bufferedReader = new BufferedReader(new InputStreamReader(
- connection.getInputStream()));
- result = bufferedReader.readLine();
- } catch (IOException e) {
- throw new RuntimeException("読み込み時の例外", e);
- }
- Map<string, string=""> responseParameters = new HashMap<string, string="">();
- for (String param : result.split("&")) {
- int equalsAt = param.indexOf('=');
- if (equalsAt > 0) {
- String name = param.substring(0, equalsAt);
- String value;
- try {
- value = URLDecoder.decode(param.substring(equalsAt + 1),
- CHARSET);
- } catch (UnsupportedEncodingException e) {
- throw new RuntimeException("エンコード時の例外", e);
- }
- responseParameters.put(name, value);
- }
- }
- return responseParameters;
- }
- /**
- * 認証Urlの作成
- * @return
- */
- public String createAuthorizarionUrl() {
- //リクエスト
- Map<string, string=""> reply = sendRequest(null);
- String requestToken = reply.get("oauth_token");
- //認証Urlの作成
- String authorizationUrl = authorizationUrlBase + "?oauth_token=" + requestToken;
- //TokenSecretを設定する
- tokenSecret = reply.get("oauth_token_secret");
- return authorizationUrl;
- }
- /**
- * TokenSecretの取得
- * @return
- */
- public String getTokenSecret() {
- if ( tokenSecret == null ) {
- throw new NullPointerException("TokenSecretが設定されていない");
- }
- return tokenSecret;
- }
- }
- </string,></string,></string,></string,></string,></string,></string,></string,>
まずコンストラクタで各種固定値の設定を行います。
- oauth_consumer_key:KEY値
- oauth_signature_method:「PLANTEXT」「HMAC-SHA1」のどちらか
- oauth_timestamp:現在の秒
- oauth_nonce:ランダムの値(リクエスト毎に一意)
- oauth_version:「1.0」固定
Evernoteのサンプルではoauth_signatureにSECRET値を指定していますが、
HMAC-SHA1の際は暗号化を行うので、まだ指定していません。
oauth_signature_methodはPLANTEXT、HMAC-SHA1が存在し、サンプルはPLANTEXTが指定されています。
PLANTEXTを推奨(つうか使って)しているのも珍しい気もします。
HTTPにアクセスする時に利用するMapをTreeMapを利用していますが
暗号化時に引数をソートする必要があるのでTreeMapでKey値のソートを行っているわけです。
コンストラクタで設定した後はコールバックUrl(oauth_callback)を指定します。
Evernoteの認可画面により処理が行われるとそのUrlに戻ってきます。
認可画面に行くためにリクエストトークンを取得しに行きます。
createAuthorizarionUrl()ですね。
ここでsendRequest()でEvernoteにアクセスしています。
oauth_signatureをcreateSignatuer()を行ってHMAC-SHA1の暗号化を行って指定しています。
返ってきたResponseから値を取得します。
レスポンスにリクエストトークン(oauth_token)とSecret(oauth_token_secret)が指定されています。
リクエストトークンはhttps://sandbox.evernote.com/?oauth_token=xxxxとして利用すると認可画面にアクセスする事ができます。
Secret値は、アクセストークン時にシグネチャ値のキー値とする時にConsumerSecretと合わせて使用します。
ここまでが認可画面のリンクを作成する流れです。
コールバックURLを指定していますが、認可画面にアクセスし、操作を行うとこのUrlに戻ってきます。
そちらの処理が以下になります。
認証に成功するとコールバックURLにoauth_token、oauth_verifierが付与されて来ます。
oauth_verifierが存在しない場合は認証で拒否された等ですので、エラー画面等を出力しましょう。
設定されていたら、アクセストークンを取得する為、
このクラスをnewしてインスタンス化します。※設定する値は一緒。
oauth_tokenをsetToken() ,oauth_verifierをsetVerifier()を利用して、設定します。
設定したらsendRequest()によりアクセストークンを取得するのですが、
この時に最初のアクセスで取得したtokenSecretを渡します。
取得に成功するとoauth_tokenにアクセストークン、edam_shardにEvernoteにアクセスする時に必要な値が入ってきます。
このアクセストークンさえあれば何でもできるわけです。
その値を利用してAPIのアクセスです。
thriftのライブラリを使っています。
- String noteStoreUrl = noteStoreUrlBase + shardId;
- THttpClient noteStoreTrans = new THttpClient(noteStoreUrl);
- TBinaryProtocol noteStoreProt = new TBinaryProtocol(noteStoreTrans);
- NoteStore.Client noteStore =
- new NoteStore.Client(noteStoreProt, noteStoreProt);
- List<notebook> notebooks = noteStore.listNotebooks(accessToken);
- for (Notebook notebook : notebooks) {
- logger.info("Notebook: " + notebook.getName());
- }
- ook>
サンドボックス+"/edam/note/"+edam_shardの値を作成して
それでTHttpClientを生成、それを利用してTBinaryProtocolを生成しています。
それを利用して生成しているNoteStore.ClientはEvernoteのライブラリですね。
生成したオブジェクトでAPI呼び出しを行っています。
ここではlistNotebooks()です。そこにアクセストークンを渡してアクセスしています。
・・・さて、再度理解を深める為にOAuthをしっかり説明してみました。
説明がヘタすぎますけど、ご了承ください。
まぁとにかく次回はAPIで色々やってみます。
0 件のコメント:
コメントを投稿