OAuth ClientモジュールがGAEで動かない
Play! frameworkのOAuth ClientモジュールがGoogle App Engineでうまく動きませんでした。
検証環境
問題点
理由は、OAuth Clientモジュールの内部でplay.libs.WSを使っているからです。play.libs.WSは、org.apache.commons.httpclientを使い、これがGAEではサポートされおらず、次のようなエラーをログに残します。
RuntimeException occured : java.security.AccessControlException: access denied (java.lang.RuntimePermission modifyThreadGroup)
OAuthの認証自体はできたのですが、例えばTwitterにつぶやきをPOSTすると上記のエラーが発生しました。
String statusUrl = "http://api.twitter.com/1/statuses/update.xml?status=" + URLEncoder.encode("This is a pen.", "utf-8"); getConnector().sign(user, WS.url(statusUrl), "POST").post(); // getConnectorは、OAuthClientクラスのインスタンス
解決方法
そこで、このモジュールに同梱されているsignpost-core-1.2.jarを直接使うことにしました。
OAuthConsumer consumer = new DefaultOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET); URL url = new URL("http://api.twitter.com/1/statuses/update.xml?status=" + URLEncoder.encode("This is a pen.", "utf-8")); HttpURLConnection statusRequest = (HttpURLConnection) url.openConnection(); statusRequest.setRequestMethod("POST"); consumer.setTokenWithSecret(user.getToken(), user.getSecret()); consumer.sign(statusRequest);
その他
このモジュールをGAEで使うため、いくつか修正をおこないました。
GAE用を作成
オリジナルのOAuth Clientモジュールを修正するので、Play! frameworkのmodulesディレクトリにあるoauth-1.0をコピーし、oauth-gaeとしました。
Credintialsモデルの削除
GAEでSienaを使うため、また不必要なのでoauth-gae/app/models/oauthclient/Credentials.javaは削除しました。
バグの修正
oauth-gae/app/play/modules/oauthclient/OAuthClient.javaのretrieveAccessTokenメソッドにuser.setTokenが2回実行されるバグ(setSecretが無い)があるので修正しました。
public void retrieveAccessToken(ICredentials user, String verifier) throws Exception { Logger.info("Token before retrieve: " + getConsumer(user).getToken()); Logger.info("Verifier: " + verifier); getProvider().retrieveAccessToken(getConsumer(user), verifier); user.setToken(consumer.getToken()); // user.setToken(consumer.getTokenSecret()); user.setSecret(consumer.getTokenSecret()); }
再ビルド
モジュールのルートディレクトリに戻って、antを実行し、oauth-gae/libに新しいplay-oauthclient.jarを作成しました。
Siena用のUsersモデル
Siena用のICredentialsインターフェースを実装したUsersモデルを用意しました。
package models; import siena.*; import play.modules.oauthclient.ICredentials; @Table("Users") public class Users extends Model implements ICredentials { @Id(Generator.AUTO_INCREMENT) public Long id; public String username; private String token; private String secret; public Users(String username) { this.username = username; insert(); } public static Users find(String username) { return Model.all(Users.class).filter("username", username).get(); } public static Users findOrCreate(String username) { Users user = find(username); if (user == null) { user = new Users(username); } return user; } public void setToken(String token) { this.token = token; update(); } public String getToken() { return token; } public void setSecret(String secret) { this.secret = secret; update(); } public String getSecret() { return secret; } }