Wicketで始めるオブジェクト指向ウェブ開発

第9回 WicketによるOAuth認証

この記事を読むのに必要な時間:およそ 10 分

LoginProcessorUrlCodingStrategyを実装する

IRequestTargetUrlCodingStrategyの実装クラスを直接作るのは骨の折れる作業です。そのため,実際にはAbstractRequestTargetUrlCodingStrategyクラスを継承するほうが便利です。このクラスを継承すれば,リクエスト・ターゲットをURL文字列に変換するencode(),RequestParametersオブジェクトを元にリクエスト・ターゲットを生成するdecode(),あるリクエスト・ターゲットがそのIRequestTargetUrlCodingStrategy実装で処理可能かどうかを判定するmatches()の3つのメソッドを実装することで,きちんと機能するクラスを作成できます。

また,IRequestTargetUrlCodingStrategyは,ページと同じように,マウントすることによってURLと結びつけられます。結びつけられたURL情報は,AbstractRequestTargetUrlCodingStrategyのgetMountPath()メソッドによって取り出すことができます。

典型的な実装は次のようになります。LoginProcessorUrlCodingStrategyの実際のプログラムです。

リスト4 AbstractRequestTargetUrlCodingStrategyサブクラスの典型的な実装

public CharSequence encode(IRequestTarget requestTarget) {
  if (!(requestTarget instanceof LoginProcessor)) {
          throw new IllegalArgumentException("this encoder can only be used with instances of " 
    + LoginProcessor.class.getName());
  }
  AppendingStringBuffer url = new AppendingStringBuffer(40);
  url.append(getMountPath());
  appendParameters(url, ((LoginProcessor)requestTarget).getParameterMap());
  return url;
}

public IRequestTarget decode(RequestParameters requestParameters) {
  return new LoginProcessor(requestParameters);
}

public boolean matches(IRequestTarget requestTarget) {
  return requestTarget instanceof LoginProcessor;
}

decode()メソッドとmatches()メソッドはたった1行ですから分かりやすいでしょう。encode()は若干長いですが,重要なコードは3行だけです。AppendingStringBufferオブジェクト(Wicketの提供する,StringBuilderと似たクラス)を作成し,getMountPath()で取り出した,マウント先URLをまず設定します。さらにAbstractRequestTargetUrlCodingStrategyクラスの提供するappendParameters()メソッドをつかって,AppendingStringBufferにパラメータを追加します。encode()メソッドの実装はおおむねこのクラスに近い実装になることが多いでしょう。

このクラスによって,URLへのアクセスはLoginProcessorへのアクセスに変換されます。

LoginProcessorを実装する

LoginProcessorの実装するIRequestTargetインタフェースには,リクエストを実際に処理するrespond()メソッドと,リクエスト処理後の解放処理を行うためのdetach()メソッドが定義されています。この2つを実装すればいいのです。今回は解放処理が必要ないため,detach()メソッドの中身は空のままです。

リスト5 LoginProcessorの実装

RequestParameters parameters;

public LoginProcessor(RequestParameters parameters) {
  this.parameters = parameters;
}

public void respond(RequestCycle requestCycle) {
  String[] values = (String[]) parameters.getParameters().get("oauth_verifier");
  if(values == null || values.length == 0) throw new IllegalStateException("'oauth_verifier' parameter is missing.");
  if(values.length > 1) throw new IllegalStateException("'oauth_verifier' parameter has plural values. the parameter should has only one value.");
  
  String pin = values[0];
  AppSession session = (AppSession) requestCycle.getSession();
  if(session.login(pin, requestCycle.getResponse())){
    String targetUrl = session.getLastAccessUrl();
    throw new RedirectToUrlException(targetUrl);
  } else {
    throw new IllegalStateException("Can not login");
  }
}

LoginProcessorのコンストラクタは,LoginProcessorUrlCodingStrategyからRequestParametersオブジェクトを受け取り,フィールドに格納します。RequestParametersは,その名の通り,URLに含まれるパラメータを表すオブジェクトです。URLの生の情報ではなく,URLが表すWicketコンポーネントのIDなど,より細かな,具体的な情報が格納されています。

URLのパラメータ値はgetParameters()メソッドで取り出すことができます。パラメータ・キーと値が格納されたMapが返却されます。

Twitterの認証画面からリダイレクトされた際に,パラメータには「PINコード」と呼ばれる値が埋め込まれています。LoginProcessorはこのPINコードを使って処理を行います。

実際にアクセスを処理するのはrespond()メソッドです。respond()メソッドでは,getParameters()メソッドで取り出したMapから「oauth_verifier」というパラメータを取り出します。これが「PINコード」と呼ばれている値です。

Mapから取り出した値は,Stringではなく,Stringの配列である点に注意してください。リクエスト・パラメータは1つのキーに対して複数の値を持つことができるため,値は配列となっています。oauth_verifierについては値は常に1つなので,配列の1つ目の要素だけを取り出します。

PINコードが手に入れば,あとの処理はすべてAppSessionで行います。AppSessionのloginを呼び出すことで,セッション内部にTwitterオブジェクトを生成します。

AppSessionのlogin()メソッドの実装についてはこの後すぐに見ます。ここでは,Twitterオブジェクトの生成が成功すればtrueが返るということだけを知っておけばかまいません。ログインが成功すれば,セッションに保存しておいた,ユーザがもともとアクセスしようとしていたURLへとリダイレクトします。

AccessTokenの取得とTwitterオブジェクトの生成

PINコードを受け取ったということは,ユーザがアプリケーションからのアクセスを許可したということです。いよいよ,最後の値であるAccessTokenを取得します。そのためには,いままでに使ったConsumerKey, ConsumerSecret, RequestToken,PINコードの4つの値すべてが必要になります。

リスト6 AccessTokenの取得とTwitterオブジェクトの作成

public boolean login(String pin, Response response) {
  try {
    if(requestToken == null) {
      throw new IllegalStateException("requestToken is missing.");
    }
  
    Twitter client = new TwitterClient();
    client.setOAuthConsumer(consumerKey, consumerSecret);
    
    AccessToken accessToken = client.getOAuthAccessToken(requestToken, pin);
    client.setOAuthAccessToken(accessToken);
    this.twitterSession = client;
    dirty();
    return true;
  } catch(TwitterException ex) {
    LOGGER.error("Can not setup OAuth Access Token to Twitter object.", ex);
    return false;
  }
}

getOAuthAccessToken()メソッドがAccessTokenの取得を行っています。直前にsetOAuthConsumer()を使ってConsumerKeyとConsumerSecretをTwitterオブジェクトにセットしていることにも注目してください。getOAuthAccessToken()にはRequestTokenと,先ほどLoginProcessorで取得したPINコードを渡します。AccessToken取得段階では,いままでに登場した4つの情報(ConsumerKey, ConsumerSecret, RequestToken, PINコード)がすべて登場しています。この4つが正しくなければAccessTokenは発行されません。AccessTokenというのはそれほど厳しく管理しなければいけない情報なのです。

AccessTokenが取得できれば,setOAuthAccessToken()メソッドを使って,TwitterオブジェクトにAccessTokenをセットします。AccessTokenがセットされたTwitterオブジェクトでは,許可を出したユーザでログインしたかのようにTwitterにアクセスすることができます。

これで認証処理は完了です。

著者プロフィール

矢野勉(やのつとむ)

フリーランスのプログラマ。Wicket-ja主催。

ウェブ・アプリケーションの開発を中心にさまざまな案件に関わってきました。現在はWicketによる開発を支援しています。

URLhttp://d.hatena.ne.jp/t_yano/

著書