スキップしてメイン コンテンツに移動

JavaではてなAPIのOAuth

あらすじ

ここのドキュメントにあるお話を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

コメント

このブログの人気の投稿

Javaでprivateなfieldやmethodにアクセスする

JUnitでテストしてると、privateなフィールドにアクセスして、値を参照したりセットしたりしたくなるわけですが、よく使うのでメモしておきます。 例えば、次のような対象のクラスがあるとします。 public class ParentClass { private String hoge = "ParentClass!!"; public void dispMsg() { System.out.println("dispMsg:" + hoge); } private void privateDispMsg(String msg) { System.out.println("dispMsg:" + msg); } }

GolangでWindows GUIアプリケーション

GUIアプリ作成の前提 社内ツールとしてexeで配布 開発環境はGoLandを使う 社内ツールとしてexeを配布ということであれば、Visual StudioでC#による開発だと思います。しかしながら、Go言語を習得したいのと、GoLandの補完機能が便利で、Android Studio使っていたこともあり、とっつきやすいという点からGo縛りでGUIアプリケーションを考えたいと思います。 lxn/walk Windows application library kit for Go. Windows向けしか考えていないので、まずこのライブラリなのですが、ボタンやコンボボックスが思ったように並ばなかったり、手軽にイメージボタンを配置したりなど、簡単にレイアウトを変更できない課題に直面しました。レイアウト作成だけで時間を取られてしまいます。そう言えば、Androidアプリ開発のときは、XMLでデザイン部分を切り離してたのを思い出して、今回の調査の運びとなりました。 fyne-io/fyne Cross platform native GUIs designed for Go based on Material Design. Supports: Linux, macOS, Windows, BSD, iOS and Android. walkと比べると、クロスプラットフォームで作成できるのですが、こちらもコードの中にデザインを書いていく形でした。 therecipe/qt therecipe/qt allows you to write Qt applications entirely in Go or JavaScript. Qt Creatorなるものがあるようで、デザイン部分を切り離せる印象はあるものの、最新のPCでもビルドに時間がかかるようで、学習コストもかかるという記事を見かけて断念しました。 どうしたものかとツイートしましたら、下記のようにご助言をいただいた。 同じ課題に対し色々試した結果、PWAのフルキャッシュドに落ち着いた。中身はGo+WASMで。 https://t.co/e60whDTV16 — のぼのぼ📡 (@nobonobo) July 21, 2020 PWA 早速調べてみました。Googleが進めているプロジェクトで、ネイティブアプリのよ

Eclipseの高速化メモ

Eclipseが重いと一言に言っても、いろいろな工程での話があると思いますが、過去記事のなども含めてこの記事にピックアップしておきたいと思います。以下はWindows環境での話です。