この頃やっと仕事でプログラミングやるようになったので折角だしTDDでやってみるかと思ったらEclipseにActivityのコードを自動生成されて出鼻をくじかれました。
そんなことはどうでもよくて、今日はHTTPライブラリVolleyでDigest認証を実装してみたという話。誰得感は否めない。あくまでも私のメモです。
Digest認証のながれ
- クライアントからサーバにリクエストを投げると401が返ってくる。
- 401と一緒に受け取る
WWW-Authenticate
ヘッダのパラメータrealm
やらnonce
などと、username
、password
を使ってAuthorization
ヘッダを作る。 Authorization
ヘッダにメッセージ本文をつけたりして再度サーバにリクエストを投げる。ヘッダのパラメータが正しければ200が返ってくる。
Volley内部の処理のながれ
Volley.newRequestQueue(Context)
でリクエストキューを作る。このときBasicNetwork
インスタンスが生成され、リクエストの送信に使われるようになる。- リクエストキューにリクエスト(
Request
クラスを継承したクラスのインスタンス)を追加するとNetworkDispatch
さんが順番にリクエストをさばいてくれる。 - リクエストが送られる前に
Request<T>#getHeaders()
が呼ばれる。Authorization
ヘッダなどを追加したいときはこのメソッドを自前のリクエストクラスでオーバーライドする。 BasicNetwork
のperformRequest
では401を受け取るとattemptRetryOnException
メソッドが呼ばれる。- リクエストにリトライポリシーを仕込んでおくと、指定したリトライ回数だけ同じリクエストを投げてくれる。
つまり、リトライポリシーの中でWWW-Authenticate
ヘッダを使ってAuthorization
ヘッダを作り、次回のリクエストに含めて再度投げてもらえばよいということです。
実装例
いろいろ面倒臭かったのでDefaultRetryPolicy
クラスを継承してretry
メソッドだけオーバライドした。
Request
クラスを継承したMyRequest
クラスの内部クラスです。
retry
メソッドはこのままだと認証に失敗し続けると抜け出せないので工夫してください。
Authorization
ヘッダを作成するにはDigestScheme
クラスを使うのが便利です。
ざっくりとした使い方はgetHeaders
メソッドの中をごらんください。
例外を吐くので適宜キャッチしてください。
あと、authenticate
メソッドに渡しているcredencials
にはユーザ名とパスワードが、
request
にはメソッド(GETとかPOSTとか)とURLが含まれています。
それぞれUsernamePasswordCredencials
とかHttpPost
とかを参照してください。
public class MyRequest extends Request<JSONObject> { // いろいろ略 // コンストラクタとかでDigestRetryPolicyを生成する @Override public Map<String, String> getHeaders() throws AuthFailureError { DigestScheme ds = new DigestScheme(); ds.processChallenge(new BasicHeader("WWW-Authenticate", mChallengeHeader.get("WWW-Authenticate")); Header header = ds.authenticate(credencials, request); Map<String, String> newHeader = new HashMap<String, String>(super.getHeaders()); newHeader.put(header.getName(), header.getValue()); return newHeader; } private Map<String, String> mChallengeHeader; public void setChallengeHeader(Map<String, String> header) { this.mChallengeHeader = header; } class DigestRetryPolicy extends DefaultRetryPolicy { @Override public void retry(VolleyError error) throws VolleyError { if (error.statusCode == HttpStatus.SC_UNAUTHORIZED) { setChallengeHeader(error.networkResponse.header); } else { super.retry(error); } } } }
こんなんでよいのか
正直良くわからん。いい方法があったら教えて下さい。
あとVolleyライブラリの中にAuthenticator
といういかにもなインタフェースがあってこれを使えるとかっこよさげなのですが、そもそもどういう時にどうやって使えばよいのかわからなかったのでその辺も教えてくださる方をお待ちいたしております。
書いてると正しいかどうか疑心暗鬼になっていく…処理の流れと雰囲気さえ掴んでいただければ幸いです。