arcanum_jp’s blog

おっさんの日記

Twitter botが作りたい。

Twitter4jを使ったTwitterアプリケーションで、ログイン処理を作ってみたいと思います。フレームワークに使ったのはWicketですが、URLとクエリ文字列が分かるのであれば応用ができると思います。


とりあえずこのメモの前提となったバージョンは以下の通りです。

前提となるライブラリ

ライブラリ名 バージョン
Twitter4j 2.1.12(SNAP SHOT VERSION)
Wicket 1.3.4 (もうかなり古いですが)

Twitterアプリ側の設定

 ユーザーにTwitterログイン処理を行わせるには、登録したツイッターアプリケーションの画面で「アプリケーションの種類」を「ブラウザアプリケーション」にして、コールバックURL欄に、ログインが成功した場合に呼び出されるURLを登録します。このURLが呼び出されたらログインが成功した後の処理を記述するんですね。このコールバックURL欄ですが、自宅の開発マシンなどで開発中とかでもhttp://127.0.0.1:8080/hogehage/verifyなどのプライベートなURLでもコールバックしてくれます。すごいですねぇ。

ユーザーにログインを促す

まずは、ユーザーのログインを促す処理です。OpenIDのようにID欄もコンシューマーを選択または入力するフィールドも必要ありません。リンクで十分です。押された処理として、Twitterアプリケーションのコンシューマーキーとコンシューマーキー・シークレットを使ってTwitterのログインページにリダイレクトします。


MainPage.java (例外処理は省いています)

public class MainPage extends WebPage{

    private Link _loginasuser = new Link("loginasuser", new Model("")){

        public void onClick() {

            String consumerkey    = ... // DBとかに保存したコンシューマキーを取得
            String consumersecret = ... // DBとかに保存したコンシューマシークレットを取得

            Twitter twitter = new twitter4j.TwitterFactory().getInstance();
            twitter.setOAuthConsumer(consumerkey, consumersecret);
            RequestToken reqtoken = twitter.getOAuthRequestToken();
				
            // Twitterのログインページにリダイレクト
            setResponsePage(new RedirectPage(reqtoken.getAuthorizationURL()));


        }

    };

}

コールバックURL側の処理

Twitter側でのログインが成功するとコールバックURL欄に記入したURLが呼び出されるため、WicketのナイスURLを使って検証用のページをマウントします。


MyApp.java

public class MyApp extends WebApplication {

    protected void init() {

        ... 省略

        // http://hogehate.com/veryfytwitter?oauth_token=XXXX&oauth_verifier=YYYY
        mountBookmarkablePage("veryfytwitter", VerifyAsUserPage.class);

    }
	
}


つぎにログインが成功した場合の処理です。コールバックURL欄に記載したURLにクエリ文字列としてoauth_token=xxx&oauth_verifier=yyyと言った情報が付加されてきます。この情報を使ってログインしたユーザーのアクセストークンを取得します。取得したアクセストークンの情報はDBやセッションなどに保存して、次からはコンシューマーキーとこのアクセストークンを使ってユーザーに成り代わってTwitterにアクセス可能です。


VerifyAsUserPage.java (例外処理は省いています)

public class VerifyAsUserPage extends AbstractPage{

    public VerifyAsUserPage(PageParameters params){

        // request parameters
        //oauth_token = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
        //oauth_verifier = YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY


        String oauthtoken    = params.getString("oauth_token");
        String oauthverifier = params.getString("oauth_verifier");
        if(oauthtoken == null || oauthverifier == null){
            // エラーだ、エラーいこっちゃ、エラーページにリダイレクト
            setResponsePage(new RedirectPage("..."));
            return;
        }


        String consumerkey    = ... // DBとかに保存したTwitterアプリのコンシューマキーを取得
        String consumersecret = ... // DBとかに保存したTwitterアプリのコンシューマシークレットを取得

        Twitter twitter = new twitter4j.TwitterFactory().getInstance();
        twitter.setOAuthConsumer(consumerkey, consumersecret);
        AccessToken atoken = twitter.getOAuthAccessToken(oauthtoken, oauthverifier);
                                                        ~~~~~~~~~~~~~~~~~~~~~~~~~~~
                                                             ↑           ↑
        String screenname = atoken.getScreenName();
        String acctoken   = atoken.getToken();
        String accsecret  = atoken.getTokenSecret();

        String sql = "INSERT INTO USER VALUES(SCREENNAME='" + screenname 
                                        + "', ACC_TOKEN='" + acctoken 
                                        + ", ACC_SECRET='" + accsecret + ")";

        // 変数sqlを使ってユーザーの情報を保存する処理とか...


        // ログイン後のページに*リダイレクト*する。
        setResponsePage(new RedirectPage("..."));

    }

}

処理をした後は、成功しても失敗しても結果ページに必ずリダイレクトします。しないとうっかりユーザーが認証後にF5キーなどを押した場合にこのコールバック処理が再リクエストされてしまいます。その場合、既にoauth_tokenとoauth_verifierは無効になっていますので、Twitter#getOAuthAccessToken(oauthtoken, oauthverifier);の処理でエラーになってしまいます。「あれ?さっきログイン成功したのに何で?」ってなってしまいます。


また再リクエストされる構造にしておくということは、Twitterとの接続が再度行われるので、悪意の第3者が例えばF5キーを連打するようなDos攻撃をしたとき、アプリ側の負荷は対応できたとしても、Twitterとの認証が多数発生してしまい、TwitterAPI制限に引っかかってしまいます。なので、リダイレクトが必要です。

ユーザーとしてつぶやく

 あとは、「つぶやく」ボタンを作ったりして、コールバックURL処理の中で保存したアクセストークンを使って、ユーザーに成り代わりTwitterにアクセスします。


TwitterPage.java (例外処理は省いています)

public class TwitterPage extends WebPage{

    private Button _tweet = new Button("tweet"){
        public void onSubmit() {

            String consumerkey    = ... // DBとかに保存したTwitterアプリのコンシューマキーを取得
            String consumersecret = ... // DBとかに保存したTwitterアプリのコンシューマシークレットを取得

            String acctoken  = ... // DBとかに保存したユーザーのアクセストークンを取得
            String accsecret = ... // DBとかに保存したユーザーのアクセストークン・シークレットを取得

            // Twitterインスタンスを取得する。
            ConfigurationBuilder cb = new ConfigurationBuilder();
                cb.setOAuthConsumerKey(consumerkey)
                  .setOAuthConsumerSecret(consumersecret)
                  .setOAuthAccessToken(acctoken)
                  .setOAuthAccessTokenSecret(accsecret);
            TwitterFactory tf = new TwitterFactory(cb.build());
            Twitter tw = tf.getInstance();
			
            // ユーザーになりかわり呟く
            tw.updateStatus("はろー!");					

        }
    }

}

まとめ

 上記ではWicketのコードで説明しましたが、まとめます。

Twitterアプリ側
  • 「アプリケーションの種類」を「ブラウザアプリケーション」にする。
  • コールバックURL欄に、ログインが成功した場合に呼び出されるURLを登録する。
ログイン処理
  • Twitterアプリのコンシューマキーを使ってTwitterの認証を行うサイトのURLにユーザーを導く
  • 認証が失敗した場合は、Twitter側で失敗した画面が出て終了
  • 認証が成功すると、先のコールバックURL欄に記載したURLが呼び出される。
コールバックURL側の処理
  • Twitterアプリのコンシューマキー、コールバックURLに付加されたoauth_tokenとoauth_verifierを使い、ユーザーのアクセストークン、アクセストークン・シークレットを取得する。
  • 無効なoauth_tokenとoauth_verifierが入力された場合は、Twitter#getOAuthAccessToken(oauthtoken, oauthverifier)の時点ではねられる(エラーになる)
  • 取得したアクセストークン、アクセストークン・シークレットをDBなどに保存する。
  • コールバックURLの処理が成功しても失敗しても、エラーなり適宜結果のページにリダイレクトする
ユーザーとしてつぶやく