ふりぶろぐ
Web Engineer's Blog
Java

【Java】Play framework 2.7でTwitter APIを使ってOAuth認証をしてみる【Twitter4J】

Java

久しぶりにTwitter APIを触ってみようと思ったので触ってみます。

以前、作成したCacheUtilsも使用しています。

ソースコードを貼り付けていますが、GitHubにも上げておきます。

概要

Play framework 2.7でTwitter APIを使ってみました。

以前、個人的に触ったときはライブラリを使わなかったのですが、今回はTwitter4Jというライブラリを使いました。

やっぱりライブラリを使うと楽ですね。

シンプルな作りになっているのでわかりやすかったです。

ユーザ情報を取得する

今回はOAuth認証を行ってログインし、ユーザ情報を取得したいと思います。

Consumer Key, Consumer Secretの取得

私は以前、取得したものを使用します。

取得方法も大きく変わって、今では簡単に取得はできなくなってしまったみたい。

解説しているページはいろいろあると思うので、今回は省略します。

Twitter4Jのインポート

build.sbtに1行追加してTwitter4Jをインポートします。

libraryDependencies += "org.twitter4j" % "twitter4j-core" % "4.0.7"

Consumer Key, Consumer Secretの設定

先ほど取得してもらったConsumer KeyとConsumer SecretをPlay側に設定します。

confディレクトリの中にtwitter4j.propertiesというファイルを作成して、以下のように設定します。

XXXXは自分のConsumer KeyとConsumer Secretに置き換えてください。

oauth.consumerKey=XXXXXXXXXXXXXXXXXXXX
oauth.consumerSecret=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

TwitterServiceを定義

Twitter4Jのメソッドはすべてthrowsが定義されているので(たぶん)ラップしておきます。

とりあえず使いそうなメソッドをラップしておきました。

あとで気づいたのですが、getOAuthRequestToken()は不要でした。

package services;

import exceptions.ForbiddenException;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.User;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;

import javax.inject.Singleton;
import java.util.Optional;

/**
 * TwitterService
 */
@Singleton
public class TwitterService {

    private static Twitter twitter = TwitterFactory.getSingleton();

    /**
     * リクエストトークンを取得する
     * @return RequestToken
     */
    public static RequestToken getOAuthRequestToken() {
        try {
            return twitter.getOAuthRequestToken();
        } catch (TwitterException e) {
            throw new ForbiddenException();
        }
    }

    /**
     * アクセストークンを取得する
     * @param requestToken リクエストトークン
     * @param oauthVerifier 認証コード
     * @return AccessToken
     */
    public static AccessToken getOAuthAccessToken(RequestToken requestToken, String oauthVerifier) {
        try {
            return twitter.getOAuthAccessToken(requestToken, oauthVerifier);
        } catch (TwitterException e) {
            throw new ForbiddenException();
        }
    }

    /**
     * アクセストークンを登録する
     * @param accessToken アクセストークン
     */
    public static void setOAuthAccessToken(AccessToken accessToken) {
        twitter.setOAuthAccessToken(accessToken);
    }

    /**
     * ユーザー情報を取得する
     * @return User
     */
    public static User getUser() {
        try {
            return twitter.verifyCredentials();
        } catch (TwitterException e) {
            throw new ForbiddenException();
        }
    }

    /**
     * ユーザー名を取得する
     * @return Optional<String>
     */
    public static Optional<String> getUserNameOptional() {
        String name = null;
        try {
            name = getUser().getName();
        } catch (Exception e) {
            // none
        }
        return Optional.ofNullable(name);
    }

    /**
     * ログイン済みかどうか
     * @return boolean
     */
    public static boolean isLogin() {
        return getUserNameOptional().isPresent();
    }
}

Controllerの実装

TwitterServiceを使ってログイン部分のControllerを実装します。

login()にアクセスするとTwitterの認証画面にリダイレクトし、認証後にauth()にリダイレクトされます。

ログイン時に発行するリクエストトークンは、リクエストごとにインスタンスを生成して発行する必要がありました。

生成しなかった場合、認証に失敗したりキャンセルしたりした場合に再認証が行えなくなってしまいました。

そのため、リクエストごとにインスタンスを生成し、そのインスタンスをキャッシュに入れて認証後に使用しています。

少し複雑になってしまいましたが、他の方法が思いつきませんでした。

一度認証してアクセストークンを発行してしまえば、リクエストトークンは不要なので、アクセストークンはTwitterServiceのインスタンスにセットします。

これによりTwitterServiceからTwitter APIを使用できるようになります。

package controllers;

import org.apache.commons.lang3.StringUtils;
import play.mvc.Controller;
import play.mvc.Http;
import play.mvc.Result;
import services.TwitterService;
import twitter4j.Twitter;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;
import utils.CacheUtils;

import java.util.Optional;

/**
 * ツイッター連携
 */
public class TwitterController extends Controller {
    /**
     * ログイン画面
     */
    public Result login(Http.Request request) {
        // ログイン済みの場合
        if (TwitterService.isLogin()) {
            return redirect(routes.HomeController.index());
        }
        try {
            // 同じインスタンスではリクエストトークンをリフレッシュできないため、認証用のインスタンスを取得
            Twitter twitter = new TwitterFactory().getInstance();
            RequestToken requestToken = twitter.getOAuthRequestToken();
            String token = requestToken.getToken();
            String tokenSecret = requestToken.getTokenSecret();
            // キャッシュにTwitterインスタンスをセット(キーはリクエストトークン)
            CacheUtils.set(token, twitter, "60s");
            return redirect(requestToken.getAuthorizationURL())
                    .addingToSession(request, "requestToken", token)
                    .addingToSession(request, "requestTokenSecret", tokenSecret);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 認証画面
     */
    public Result auth(Http.Request request) {
        // ログイン済みの場合
        if (TwitterService.isLogin()) {
            return redirect(routes.HomeController.index());
        }

        // 認証失敗時のパラメータ
        String denied = request.getQueryString("denied");

        // 認証成功時のパラメータ
        Optional<String> requestTokenOpt = request.session().getOptional("requestToken");
        Optional<String> requestTokenSecretOpt = request.session().getOptional("requestTokenSecret");

        // 認証成功
        if (StringUtils.isBlank(denied) && requestTokenOpt.isPresent() && requestTokenSecretOpt.isPresent()) {
            try {
                String token = requestTokenOpt.get();
                String tokenSecret = requestTokenSecretOpt.get();

                // ログイン画面でキャッシュにセットしたTwitterインスタンスを取得
                Optional<Twitter> twitterOptional = CacheUtils.getOptional(token);
                if (!twitterOptional.isPresent()) {
                    return redirect(routes.TwitterController.login());
                }
                Twitter twitter = twitterOptional.get();
                // リクエストトークンの発行
                RequestToken requestToken = new RequestToken(token, tokenSecret);
                String verifier = request.getQueryString("oauth_verifier");
                // アクセストークンの発行
                AccessToken accessToken = twitter.getOAuthAccessToken(requestToken, verifier);
                // TwitterServiceのインスタンスにアクセストークンをセットする
                TwitterService.setOAuthAccessToken(accessToken);
                return redirect(routes.HomeController.index());
            } catch (Exception e) {
                e.printStackTrace();
                return redirect(routes.TwitterController.login());
            }
        } else {
            return redirect(routes.TwitterController.login());
        }
    }
}

ユーザ情報の取得

あとはユーザ情報を取得するだけです。

取得してviewで表示します。

package controllers;

import filters.UserAuth;
import play.mvc.Controller;
import play.mvc.Http;
import play.mvc.Result;
import play.mvc.Security;
import services.TwitterService;
import views.html.index;

/**
 * ホーム
 */
@Security.Authenticated(UserAuth.class)
public class HomeController extends Controller {
    /**
     * トップ
     * @return
     */
    public Result index(Http.Request request) {
        return ok(index.render(TwitterService.getUser()));
    }
}
@import twitter4j.User
@(user: User)

<h3>@user.getName</h3>
<h3>@user.getScreenName</h3>
<h3>@user.getFriendsCount</h3>
<h3>@user.getFollowersCount</h3>

まとめ

Play framework 2.7でTwitter4Jを使ってOAuth認証をしてみました。

ログインの処理が少し複雑ですね。

認証失敗時のことを考慮したら、非常に時間がかかってしまいました。

すべてのソースコードはここにあげてあります。

twitter4j.propertiesのパラメータを変えたら動くと思います。

普段Twitterを使っているのでTwitter APIを使うのは楽しいですね。

ABOUT ME
りーふ
たまにブログを書いてるWebエンジニア。 サーバーサイドメインでインフラとフロントエンドもたまにやります。 Javaが得意。 Play Frameworkが好き。 本業は迷惑をかけない程度に手を抜くスタイル。 意識高い系は苦手。