OAuth認証でTwitterを利用するWebアプリケーション(PHP PECL/oauthの場合)

以前、OAuth認証でTwitterを利用するWebアプリケーション(PHP twitteroauthの場合)をいう記事を書いたのですが、状況がいろいろ変わってきたので、PECL/oauthを使う方法を紹介します。

TwitterのAPIが変わっても、Twitter専用ライブラリがそれに対応するとは限らないので、汎用のOAuthライブラリを使っておいた方がいいかもしれない、という話です。

PECL/oauthを導入してから先に進んでください(参考:PECL/oauthの導入方法)。

アプリの登録

Twitterにアプリを登録し、Consumer KeyとConsumer Secretを取得します。

アプリを一度許可したユーザが確認画面をスキップできるように、つまり「oauth/authorize」ではなく「oauth/authenticate」を使いたい場合は、下のように、「Allow this applicetion…」を有効にしてください。

OAuth認証してつぶやく

3つのファイルに分けて説明します(RSSリーダーではコードが表示されないかもしれません)。

  1. twitter-oauth-start.php: OAuth認証開始
  2. twitter-oauth-end.php: OAuth認証完了
  3. twitter-oauth-post.php: つぶやき投稿

あとで1つのファイルにまとめます。

OAuth認証開始(twitter-oauth-start.php)

「Consumer Key」と「Consumer Secret」、callbackUrlは環境に合わせて書き換えてください。

実験時には、「$oauth->disableSSLChecks();」としておいてもいいでしょう。XAMPPでは、何もしないと「Peer certificate cannot be authenticated with given CA certificates」というエラーになります。Ubuntuでは、自動的に「/etc/ssl/certs/ca-certificates.crt」が読み込まれます。

OAuth認証完了(twitter-oauth-end.php)

つぶやき投稿(twitter-oauth-post.php)

ログアウト

ブラウザを再起動すればはじめに戻りますが、次のようなtwitter-oauth-logout.phpを作っておいてもいいでしょう。

1つのファイルでつぶやく

OAuth認証をしてつぶやく処理を1つのファイルにまとめると次のようになります。

PECL/oauthの導入方法

PHPでOAuth認証をするクライアントを実装するための最も標準的な方法であるはずの、PECL/oauthの導入方法を紹介します。

Ubuntuの場合

まず、以下のコマンドで必要なパッケージをインストールします。

sudo apt-get install apache2 php5 php5-dev php-pear libpcre3-dev make
sudo pecl install oauth

次に、/etc/php5/apache2/php.iniに「extension=oauth.so」を追記し、Apacheを再起動します。

phpinfo()の結果に「OAuth」があれば準備完了です。

Windows(XAMPP)の場合

  1. http://windows.php.net/downloads/pecl/releases/oauth/からバイナリ(XAMPP 1.8.1ならphp_oauth-1.2.3-5.4-ts-vc9-x86.zip)をダウンロード・展開して、php_oauth.dllc:/xampp/php/extにコピーする。
  2. c:/xampp/php/php.iniに「extension=php_oauth.dll」を追記する。
  3. Apacheを再起動する。
  4. phpinfo()の結果に「OAuth」があることを確認する。

Mac OS X

「mac pecl oauth」でググればいいかと思います。

このあたりの詳細については、拙著『Webアプリケーション』などを参照してください。

Twitter APIのパブリックタイムラインが亡くなっていました

拙著『Webアプリケーション構築入門(第2版)』の5.3節「Twitter API」は、以下のような構成になっています。

  • XMLの処理(Java)
  • JSONの処理
    • PHP
    • JavaScript
  • コラム:JSONP

Twitter APIの大部分は、OAuthと呼ばれる認証が必要なのですが、この節の目的はHTTP通信でよく使われるXML形式とJSON形式のデータの処理方法を紹介することだったので、認証の不要なパブリックタイムライン https://dev.twitter.com/docs/api/1/get/statuses/public_timeline を採用していました。

教育的には便利なこのAPIが、先日廃止されてしまったため、5.3節のほとんどのコードが動かなくなっていました。ほとんどすべてのサンプルが動く形で提供されていることを一つのウリにしている書籍で、これはいけません。

というわけで、JSON処理の部分だけ,動くように修正しました。(XML処理の部分はあきらめます。)

https://github.com/taroyabuki/webbook2/tree/master/src/05

第三者のWeb APIを書籍で使うリスクについて、お伝えしました。

Twitter4Jでつぶやく練習

拙著『Webアプリケーション構築入門』では、「Webアプリを作れるようになる前に、Webアプリを使えるようになるのが大事」という方針のもと、比較的早い段階で、JavaとPHP、JavaScriptでウェブAPIを利用する方法を紹介しています。

そこで採用したウェブAPIの一つがTwitter APIですが、入門書であることと、紙面が限られていることを理由に、OAuth認証なしでも使えるPublic Timelineだけしか紹介しませんでした。

OAuth認証を使ってつぶやく方法は、このブログで以前紹介しましたが(該当記事)、その後、Mavenを紹介したり(該当記事)、Twitterのサイト上で自分用のトークンを作れるようになったりしたので、「プログラムからつぶやく方法」を改めて書いておきましょう。

Twitterアプリの準備

  1. まず、Twitter DevelopersにTwitterアカウントでログインし、「Create a new app」をクリック、アプリケーションを登録します(コールバックURLはあとで変えられるので適当に入れておけばいいでしょう。今回は使いませんが)。
  2. つぶやくためにはWrite権限が必要なので、SettingsタブでApplication Typeを「Read & Write」に変更します。
  3. Detailsタブに戻って、「Create my access token」をクリックし、Access tokenとAccess token secretを生成します。このページに書かれているConsumer keyとConsumer secretも使います。

Mavenプロジェクトの作成

Mavenプロジェクトを作成し、pom.xmlのdependencies要素の中に次のように記述します(TwitterのためのライブラリであるTwitter4Jを使います)。(参考:『Webアプリケーション構築入門』のウェブアプリをMavenで管理する方法

<dependency>
  <groupId>org.twitter4j</groupId>
  <artifactId>twitter4j-core</artifactId>
  <version>[2.2,)</version>
</dependency>

つぶやくためのコード

つぎのようなコードでつぶやきます。

import twitter4j.*;
import twitter4j.conf.*;

public class Tweet {

  public static void main(String[] args) throws Exception {
    ConfigurationBuilder cb = new ConfigurationBuilder();
    cb.setDebugEnabled(true)
            .setOAuthConsumerKey(**Consumer key**)
            .setOAuthConsumerSecret(**Consumer secret**)
            .setOAuthAccessToken(**Access token**)
            .setOAuthAccessTokenSecret(**Access token secret**);
    TwitterFactory tf = new TwitterFactory(cb.build());
    Twitter twitter = tf.getInstance();
    twitter.updateStatus("テスト at " + (new java.util.Date()));
  }
}

はてなブックマークAtomAPIをOAuthで利用する方法(Java, scribe-java版)

はてなブックマークAtomAPIの使い方を紹介します。2010年末にOAuthをサポートしたので、ここでもそれを試します。言語はJava、ライブラリはscribe-javaを使います。前にoauth-signpostを使う方法を紹介しましたが、こちらのほうがおそらく簡単です(準備のためにクラスを1つ余計に作らなければなりませんが)。

アプリケーションの登録

アプリケーション登録ページでアプリケーションを登録し、Consumer keyとConsumer secretを取得します。

ライブラリの準備

scribe-バージョン番号.jarとcommons-codec-バージョン番号.jarを使えるようにします(このあたりの詳細については、拙著『Webアプリケーション』などを参照してください)。

APIを定義するクラス

TwitterのようなメジャーなAPIはあらかじめscribe-javaで定義されているので(一覧)、後のコードで「TwitterApi.class」などと書くだけでいいのですが、はてなはそこまでメジャーでもないようで、APIを定義するクラスを自分で作らなければなりません。

import org.scribe.builder.api.*;
import org.scribe.model.*;
import org.scribe.utils.OAuthEncoder;

public class HatenaApi extends DefaultApi10a {

  @Override
  public String getAccessTokenEndpoint() {
    return "https://www.hatena.com/oauth/token";
  }

  @Override
  public String getRequestTokenEndpoint() {
    String scope = "?scope=read_public%2Cwrite_public%2Cread_private%2Cwrite_private";
    return "https://www.hatena.com/oauth/initiate" + scope;
  }

  @Override
  public String getAuthorizationUrl(Token requestToken) {
    return String.format("https://www.hatena.ne.jp/oauth/authorize?oauth_token=%s",
            OAuthEncoder.encode(requestToken.getToken()));
  }
}

TwitterのOAuthと違うのは、どのような権利を求めるかを「score=...」という形で書いておくところです。

Access tokenとToken secretの取得

OAuthでユーザの権利を譲り受けるためのAccess tokenとToken secretを取得します。ここで紹介するコンソール上で行う方法の他に、ブラウザ上でリダイレクトを使う方法もありますが、両者の違いはTwitterの場合などと同じなので、Twitterでの方法を見れば、リダイレクトを使う方法も実現できるでしょう。

先に取得したConsumer keyとConsumer secretを下のコードに埋め込んでください。

import java.io.*;
import org.scribe.builder.*;
import org.scribe.model.*;
import org.scribe.oauth.*;

public class ScribeTokenCreator {

  public static void main(String[] args) throws Exception {
    // プロキシサーバの設定
    //System.setProperty("http.proxyHost", "proxy.example.net");
    //System.setProperty("http.proxyPort", "3128");
    //System.setProperty("https.proxyHost", "proxy.example.net");
    //System.setProperty("https.proxyPort", "3128");

    String consumerKey = ***** Consumer key *****;
    String consumerSecret = ***** Consumer secret *****;
    OAuthService service = new ServiceBuilder().provider(HatenaApi.class).apiKey(consumerKey).apiSecret(consumerSecret).build();

    Token requestToken = service.getRequestToken();
    //System.out.println(requestToken.getRawResponse());
    String authUrl = service.getAuthorizationUrl(requestToken);
    System.out.println("このURLにアクセスし、表示されるPINを入力してください。");
    System.out.println(authUrl);
    System.out.print("PIN:");

    BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
    String pin = br.readLine();

    Token accessToken = service.getAccessToken(requestToken, new Verifier(pin));
    System.out.println("Access token: " + accessToken.getToken());
    System.out.println("Token secret: " + accessToken.getSecret());
  }
}

上のコード実行するとURLが表示されるので、そのURLにブラウザでアクセスしてください。下のようなページが表示されます。scoreの記述によって内容は変わります。この例では、公開・非公開の両方の情報への読み書き権限を要求しています。

OAuthで許可を求められているところ

「許可」をクリックすると下のようなページが表示されるので、表示された文字列を先のコードからのプロンプトで入力してください。Access tokenとToken secretが得られます。

「許可」をクリックした結果

ブックマーク

譲り受けた権利を使って「http://b.hatena.ne.jp/atom/post」にURLをPOSTすれば、そのURLをはてなブックマークに登録できます。例として「http://www.google.com/」を登録するコードは次のようになります。先に取得したConsumer keyとConsumer secret、Access token、Token secretを埋め込んで実行してください。

import org.scribe.builder.*;
import org.scribe.model.*;
import org.scribe.oauth.*;

public class ScribeBookmark {

  public static void main(String[] args) throws Exception {
    // プロキシサーバの設定
    //System.setProperty("http.proxyHost", "proxy.example.net");
    //System.setProperty("http.proxyPort", "3128");

    // これはユーザによらない
    String consumerKey = ***** Consumer key *****;
    String consumerSecret = ***** Consumer secret *****;
    OAuthService service = new ServiceBuilder().provider(HatenaApi.class).apiKey(consumerKey).apiSecret(consumerSecret).build();

    // これはユーザごとに異なる
    Token accessToken = new Token(
            ***** Access token *****,
            ***** Token secret *****);

    // ターゲット
    String target = "http://www.google.com/";
    String xml = String.format("<entry xmlns='http://purl.org/atom/ns#'>"
            + "<title>dummy</title>"
            + "<link rel='related' type='text/html' href='%s' />"
            + "<summary type='text/plain'></summary>"
            + "</entry>",
            target);

    // HTTPリクエスト(POST)
    OAuthRequest request = new OAuthRequest(Verb.POST, "http://b.hatena.ne.jp/atom/post");
    request.addHeader("Content-Type", "application/octed-stream");
    service.signRequest(accessToken, request);
    request.addPayload(xml);
    Response response = request.send();
    
    // 結果の表示
    System.out.println(response.getCode());
    System.out.println(response.getBody());
  }
}

Twitterの場合との違うのは、リクエストボディにXMLを書かなければならないこと、それを送信するためにContent-Typeをapplication/octed-streamにしておくことです。

ブックマークの編集

ブックマークを編集したいときは、上記のPOSTのレスポンス・ヘッダ (Location) に対して、GETで現状を取得し、PUTで新しいデータを送信します。例として、現在のコメントに「♡」を追記するコードは次のようになります(javax.xml.parsers.*とorg.w3c.dom.*、org.xml.sax.*をインポートしておく必要があります)。タイトルははてな全体で共有されるので、よほどの理由がない限りは変更しない方がいいでしょう。

// 編集のためのURL
String editUrl = response.getHeader("Location");

// 現コメントの取得
// HTTPリクエスト(GET)
request = new OAuthRequest(Verb.GET, editUrl);
service.signRequest(accessToken, request);
response = request.send();
System.out.println(response.getCode());

// 結果(XML)の処理
Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(response.getStream()));
String comment = doc.getElementsByTagName("summary").item(0).getTextContent();

// コメントの更新(「♡」を付加する)
String newComment = comment + "♡";
xml = String.format("<entry xmlns='http://purl.org/atom/ns#'>"
        //+ "<title>Google 2</title>"
        + "<summary type='text/plain'>%s</summary>"
        + "</entry>",
        newComment);

// HTTPリクエスト(PUT)
request = new OAuthRequest(Verb.PUT, editUrl);
request.addHeader("Content-Type", "application/octed-stream");
service.signRequest(accessToken, request);
request.addPayload(xml);
response = request.send();

// 結果の表示
System.out.println(response.getCode());
System.out.println(response.getBody());

補足

アプリケーションに与えた許可を取り消したいときは、「Myはてな」→「ユーザ設定」→「外部アプリケーション認証」→「外部のアプリケーションから、はてなのサービスを利用する」の順にクリックしてください。「https://www.hatena.ne.jp/はてなID/config/auth/provider」にアクセスしてもいいでしょう。