解決方法
java.io.NotSerializableException - How to solve Not Serializable Exception | Examples Java Code Geeks
import java.io.Serializable;
public class TransientExample implements Serializable {
private static final long serialVersionUID = 6128016096756071380L;
private transient Pair pair = null;
public TransientExample(String key, Integer value) {
this.pair = new Pair(key, value);
}
@Override
public String toString() {
return pair.toString();
}
}
適当なクラスを作って、
・implements Serializableつける
・問題のあるクラスをtransient で定義しなおす
でエラーきえた。
~~~
あれ、逆か。transientは不要っぽい。
それと、そもそもOAuthConsumerをSerializeしようとしていてエラーが発生したのがきっかけだが、少し複雑な感じ。
結論からいくと、OAuthConsumerはサーバ側で保持する必要はなく、Providerだけ保持していればいいっぽい。
Issue 11 - oauth-signpost - Make OAuthProvider and OAuthConsumer serializable - Simple OAuth message signing for Java - Google Project Hosting
そして、OAuthConsumerはnewで初期化した時点ではserialize可能だが、たぶんproviderと混ぜてURL生成なんかをしていると、途中からserializeできなくなる。(しようとするとnullが返された。serializeするライブラリによってはエラーを吐く)
url = provider.retrieveRequestToken(consumer, callback);
~~~
最終的にうまくいったのでまとめ。
oauthの各ステップ
- STEP01:認証URLの生成
- consumerとproviderを初期化
- sessionやDBとかでproviderは保持しておく
- consumerが持っているaccessTokenKeyとSecペアをsessionやDBで保持しておく(これがわかりにくかった)
- STEP02:認証ページからのCALLBACK
- callback先に認証キーがGETパラメータで返ってくるので、それをsessionとかDBで保持しておく
- STEP03:利用
- providerは保持しておいたやつをそのまま使う
- consumerはSTEP01と同様に初期化した後、STEP01で保持しておいたaccessTokenKeyとSecをconsumerに適用する(これがわかりにくかった)
- providerにconsumerとSTEP02で得た認証キーを適用する
- 後は公式通りに利用
公式には
consumer.setTokenWithSecret(ACCESS_TOKEN, TOKEN_SECRET);
とだけあり、ACCESS_TOKENやTOKEN_SECRETは、callbackで得られるkeyとsecのペアのことかと思っていた。
しかし、これはSTEP01でcosumerが持っているやつを使わなければいけない。
STEP01
OAuthConsumer consumer = new DefaultOAuthConsumer("XXX","XXX");
OAuthProvider provider = new DefaultOAuthProvider("XXX","XXX");
authUrl = provider.retrieveRequestToken(consumer, CALLBACK_URL);
System.out.println(consumer.getToken());
System.out.println(consumer.getTokenSecret());
STEP02
STEP03
OAuthProvider provider = (OAuthProvider)session.getAttribute("provider");
OAuthConsumer consumer = new DefaultOAuthConsumer("XXX","XXX");
consumer.setTokenWithSecret("STEP01のaccessTokenKey", "STEP01のaccessTokenSec");
provider.retrieveAccessToken(consumer, "STEP02で認証元からうけとった認証キー");
URL url = new URL("認証が必要なリソースURL");
~~~
さらにハマった。。
上の手順だとSTEP03を2回以上くりかえすと、2回目からは401がでてしまう。
理由は、retrieveAccessTokenするとaccessTokenとaccessTokenSecretが更新されるから。
なので、
STEP02でretrieveAccessTokenを済ませて置き、そこで新しく発行されるtokenを保持し、STEP03で何度も同じものを使うというふうにしなければいけない。
STEP01
OAuthConsumer consumer = new DefaultOAuthConsumer("XXX","XXX");
OAuthProvider provider = new DefaultOAuthProvider("XXX","XXX");
authUrl = provider.retrieveRequestToken(consumer, CALLBACK_URL);
System.out.println(consumer.getToken());
System.out.println(consumer.getTokenSecret());
session.setAttribute("provider", provider);
STEP02 (retrieveAccessTokenを済ませて置き、そこで新しく発行されるtokenを保持)
OAuthProvider provider = (OAuthProvider)session.getAttribute("provider");
OAuthConsumer consumer = new DefaultOAuthConsumer("XXX","XXX");
provider.retrieveAccessToken(consumer, "GETパラメータでうけとった認証キー");
System.out.println(consumer.getToken());
System.out.println(consumer.getTokenSecret());
STEP03
OAuthConsumer consumer = new DefaultOAuthConsumer("XXX","XXX");
consumer.setTokenWithSecret("STEP02のaccessTokenKey", "STEP02のaccessTokenSec");
URL url = new URL("認証が必要なリソースURL");