Play 的に正しいと思われる解決方法が明らかになったのでメモ。Play framework 1.2.5 が対象です。また、フロントの Web サーバは nginx を想定しています。
リバースプロキシと SSL
Play framework を使って開発された Web アプリケーションを本番環境で運用する場合、Play アプリの AP サーバを直接外部に対して公開するのではなく、Apache や Nginx などの Web サーバを前方に立てて、その Web サーバからの HTTP リクエスト(リバースプロキシ)として、Play アプリの AP サーバに HTTP リクエストが到達するようなサーバ構成を取ることが一般的と思われます。
Play に限った話ではありませんが、このようなサーバ構成において SSL (HTTPS) による通信をサポートするとなると、フロントの Web サーバで SSL をデコードし、Web サーバと AP サーバの間を HTTP プロトコルで通信をすることがきっと多いことでしょう。
Play framework でのリダイレクトと HTTPS に関わる問題
ところで、Play framework の play.mvc.Controller クラス (MVC アーキテクチャでいうところのコントローラに相当するもの) には、HTTP ステータスコード 301/302 によりリダイレクトを行う Controller.redirect()
メソッドが実装されています。このメソッドを使って redirect("Users.list");
とすると、次のような HTTP レスポンスがクライアントに返却され、その結果、http://localhost:9000/Users/list
の URL へのリダイレクトが行われることになります。
HTTP/1.1 302 Found Server: nginx/1.2.2 Date: Mon, 27 Aug 2012 16:30:14 GMT Content-Type: text/plain; charset=utf-8 Content-Length: 0 Connection: close Location: http://localhost:9000/Users/list Cache-Control: no-cache
この Controller.redirect()
によるリダイレクトですが、前述したリバースプロキシかつフロントの Web サーバで SSL デコードをする構成で行うと、リダイレクト先の URL が https で始まる URL ではなく、http で始まる URL となってしまいます。これにより、Web アプリケーションを利用している最中のリダイレクトにより、思いがけず HTTPS から HTTP に切り替わってしまうという悩ましい問題が発生し得ることになります。この問題は、Play アプリとフロントの Web サーバとの間のやりとりが HTTP ベースで行われていることによります。
問題の解決方法
この問題を解決する方法として、
- フロント Web サーバの設定と Play アプリの application.conf の設定を変更する
Controller.redirect()
メソッドを呼び出す直前で、request.isSecure = Boolean.TRUE
とする
application.conf の設定を変更する
リダイレクト先の URL を生成する Play framework の当該処理の実装を見てみるとわかりますが、スキームを http にするか https にするかの判断は、play.mvc.Http
クラスの Http#parseXForwarded()
メソッド と Http#isRequestSecure()
メソッド の処理結果が強く関係してきます。特に、後者のメソッドが実行されて true を返すような処理となる場合に、リダイレクト先 URL のスキームが https となるので、つまりはそのような望ましい結果となるように条件を揃えてあげればいいことになります。
同式を true にするために変更すべき application.conf の設定は、以下の2つになります。
XForwardedSupport=all
を追記する ... これにより、Play アプリに渡される HTTP リクエストのヘッダにX-Forwarded-For
フィールドが存在すれば、無条件にHttp#isRequestSecure()
が実行されるようになります。XForwardedProto=https
とする ... これを設定することにより、X-Forwarded-Proto
フィールドやX-Forwarded-Ssl
フィールドの値によらず、X-Forwarded-For
フィールドが定義された状況下での Play アプリに対するリクエストを HTTPS でリクエストされたものとして扱うようになります。Web アプリ全体を HTTPS で提供する場合に適した設定となります。
nginx.conf の設定を変更する
nginx.conf の設定は簡単で、リバースプロキシの設定において
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
を仕込んであげれば OK です。
解決方法は以上となります。もっとこと細かに HTTP / HTTPS を制御したい、としても、基本は play.mvc.Http
クラスの、特に前述した2つのメソッドの実装を眺めれば解決するものと思います。
0 コメント:
コメントを投稿