Android、Java、Web系、Linux、マラソン等の備忘録

2013/09/28

JavaではてなAPIのOAuth

0 件のコメント

あらすじ

ここのドキュメントにあるお話をJavaでやってみようと
・Consumer key を取得して OAuth 開発をはじめよう - Hatena Developer Center
http://developer.hatena.ne.jp/ja/documents/auth/apis/oauth/consumer
サンプルコードもあるんですけど、Perl版とRuby版しかなくてどっちもあまりやったことないので一筋縄にいきませんでした。

OAuthのJavaライブラリ

OAuthのライブラリは、oauth-signpostを使いました。
・oauth-signpost - Simple OAuth message signing for Java - Google Project Hosting
http://code.google.com/p/oauth-signpost/
アクセストークン取得までは、GitHubにあるTwitterのサンプルですが、これを参考にサクッと取得ができました。
・signpost-examples/OAuthTwitterExample/src/TwitterMain.java at master · mttkay/signpost-examples · GitHub
https://github.com/mttkay/signpost-examples/blob/master/OAuthTwitterExample/src/TwitterMain.java

OAuth認証

Twitterのサンプルとほとんど一緒なんですが、はてな用のコードを載せておきます。
package net.takaiwa.sample.hatena;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

import oauth.signpost.OAuth;
import oauth.signpost.OAuthConsumer;
import oauth.signpost.OAuthProvider;
import oauth.signpost.basic.DefaultOAuthConsumer;
import oauth.signpost.basic.DefaultOAuthProvider;
import oauth.signpost.exception.OAuthCommunicationException;
import oauth.signpost.exception.OAuthExpectationFailedException;
import oauth.signpost.exception.OAuthMessageSignerException;
import oauth.signpost.exception.OAuthNotAuthorizedException;

public class HatenaSample {

    private static final String CONSUMER_KEY = "YOUR_CONSUMER_KEY";
    private static final String CONSUMER_SECRET = "YOUR_CONSUMER_SECRET";

    private static final String REQUEST_TOKEN_ENDPOINT_URL =
        "https://www.hatena.com/oauth/initiate";
    private static final String ACCESS_TOKEN_ENDPOINT_URL =
        "https://www.hatena.com/oauth/token";
    private static final String AUTHORIZATION_WEBSITE_URL =
        "https://www.hatena.ne.jp/touch/oauth/authorize";

    private OAuthConsumer mConsumer =
        new DefaultOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);

    public String mAccessToken = null;
    public String mAccessTokenSecret = null;

    /**
     * OAuth認証開始
     */
    public void startOAuth() {

        OAuthProvider provider =
            new DefaultOAuthProvider(REQUEST_TOKEN_ENDPOINT_URL + "?scope=read_public",
                    ACCESS_TOKEN_ENDPOINT_URL,
                    AUTHORIZATION_WEBSITE_URL);
        try {
            String authUrl = provider.retrieveRequestToken(mConsumer,
                    OAuth.OUT_OF_BAND);

            System.out.println("Request token: " + mConsumer.getToken());
            System.out.println("Token secret: " + mConsumer.getTokenSecret());

            System.out.println("ブラウザとかで開いて認証するURL:" + authUrl);

            System.out.println("PIN:");

            BufferedReader br = new BufferedReader(
                    new InputStreamReader(System.in));
            // PIN入力待ち
            String pin = br.readLine();
            // PIN入力後の処理
            provider.retrieveAccessToken(mConsumer, pin);

            mAccessToken = mConsumer.getToken();
            mAccessTokenSecret = mConsumer.getTokenSecret();

            System.out.println("Access token:" + mAccessToken +
                    " Token secret:" + mAccessTokenSecret);

        } catch (OAuthMessageSignerException e) {
            e.printStackTrace();
        } catch (OAuthNotAuthorizedException e) {
            e.printStackTrace();
        } catch (OAuthExpectationFailedException e) {
            e.printStackTrace();
        } catch (OAuthCommunicationException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

ほとんど一緒と言いながら決定的な違いというかはてな仕様があるんですけど、アクセストークン取得だけなら、Twitterサンプルの値を変えれば良いだけです。その違いというのが、44行目の「?scope=read_public
・はてな OAuth scope (許可操作) 一覧 - Hatena Developer Center
http://developer.hatena.ne.jp/ja/documents/auth/apis/oauth/scopes#read_public
はてなのドキュメントにスコープ(権限を指定するやつ)ってのがあって、これをどこで?ライブラリoauth-signpostに対してどうやって指定するんだ?と小一時間悩みました。しかも、アクセストークン取得は難なくできて、次のアクセストークンを使ってユーザ情報を取得サンプルで、「401 Authorization Required」というレスポンスが返ってきたので、ユーザ情報取得のコードが間違ってると思ってたんですが、実はそもそも認証でスコープを与えてなかったというのが今回の敗因です。

サンプルコードの解説ですが、実行するとコンソールにauthUrlが表示されます。それをブラウザにコピぺして認証画面へ

許可ボタンを押すと、下図のようなPINコードが表示されます。


コンソールはこのPINコード入力待ちになっているので、コンソールへコピぺしてエンターを押すとアクセストークンが取得できます。

はてなユーザ情報取得

上記HatenaSampleクラスに追加して実行するサンプルです。先ほどのアクセストークンがmAccessTokenとmAccessTokenSecretに入っている前提です。

/**
 * ユーザ情報の取得
 */
public void dipUserInfo() {

    mConsumer.setTokenWithSecret(mAccessToken, mAccessTokenSecret);

    try {
        // HTTPリクエスト
        URL url = new URL("http://n.hatena.com/applications/my.json");

        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setRequestMethod("GET"); // HTTPメソッドはPOST
        mConsumer.sign(con); // リクエストに署名

        con.connect();

        // レスポンスコード
        System.out.println(con.getResponseCode() + " " +
                con.getResponseMessage());

        BufferedReader reader =
              new BufferedReader(new InputStreamReader
                  (con.getInputStream(), "JISAutoDetect"));

        String buffer = reader.readLine();
        System.out.println();
        while (null != buffer) {
            System.out.println("JSON:" + buffer);
            buffer = reader.readLine();
        }

        con.disconnect();

    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (OAuthMessageSignerException e) {
        e.printStackTrace();
    } catch (OAuthExpectationFailedException e) {
        e.printStackTrace();
    } catch (OAuthCommunicationException e) {
        e.printStackTrace();
    }
}

サンプルなのでアクセストークンはメモリ上のみですが、認証後は保存するなりして使い回せるようにした方がいいでしょう。

oauth-signpost以外のやり方

oauth-signpostライブラリ以外では、scribe-javaを使うやり方が見つかりました。
・fernandezpablo85/scribe-java · GitHub
https://github.com/fernandezpablo85/scribe-java
こちらの方が簡単に書けるよとのこと
早々にこちらのサンプルを試しておけばサクッとユーザ情報も取得できたかもしれないですけど、Java標準のHttpURLConnectionでなんでユーザ情報が取得できないんだよ?Twitterサンプルと何が違うんだよ!?と駆り立てられるものがありましたから、小一時間悩む事となってしまいました。

余談

サンプルコードをググろうとすると、関係ないはてなブログ(or はてなブックマーク) + APIというキーワードの記事がヒットする感じで、求めてるのは、はてなAPIなんですが、はてなというキーワードのサービスが多いために検索結果にノイズが多いですね。

参考

PR

0 件のコメント :

コメントを投稿