JettyにSecurityを。DIGEST認証。

Security、大事ですね。
社内イントラだから、大丈夫じゃねとか多いと思います。
まぁ、確かにそうかもしれませんが、
性悪説にたつと色々考える必要があります。

ということで、JettyのSecutiryを色々、設定してみたいと思います。
AWSに構築しているので、まずは、ファイアウォールです。
が、
ec2のsecurity groupで入りは、弾けそうですが、
社内やら、自宅やら、E-Mobileやらアクセス元が多数あるので、
後回しにします。iptablesも後回し。
まずが、アプリケーション自体、Jettyの設定をやりたいと思います。

デフォルトのままだと、
http://localhost:8080:/hoge/piyo/
とかで、ディレクトリ丸見えなので、コレ見えなくしたい。
とゆー事で、認証かけます。
Basic、Digest、NTLM、OAuthなどありますが、
×NTLM→Windows認証なので、いらない。
×OAuth→TwitterFacebookのOAuthの認証実装した事あるけど、Oauth2ならシンプルだけど、トークン面倒。
      いかんせんアプリケーション側の変更もそれなりにあるので、ちょっと大変。
となると、Basic認証か、Digest認証の2択ですが、
Basicよりは、Digestだろって事で、Digest認証でいきます。
っとその前に、Flex(Air)でDigest認証いけるのか?
ネットでは、Basic認証のサンプルたくさんあったけど・・。
http://livedocs.adobe.com/flex/3_jp/langref/flash/net/URLRequest.html
「 HTTP 認証 (基本 / ダイジェスト)、Windows 統合認証 (NTLM および Kerberos を含む)、SSL 証明書認証」
大丈夫っぽい。

まず本家のドキュメントみる。http://wiki.eclipse.org/Jetty/Feature/Realms
tomcatと同じです。web.xmlとか設定してって事。
web.xml

	<!-- security setting -->
	<security-constraint>
		<web-resource-collection>
			<web-resource-name>hogeAuth</web-resource-name>
			<url-pattern>/*</url-pattern>
		</web-resource-collection>
		<auth-constraint>
			<role-name>admin</role-name>
			<role-name>user</role-name>
		</auth-constraint>
	</security-constraint>
	<login-config>
		<auth-method>DIGEST</auth-method>
		<realm-name>hogeAuth</realm-name>
	</login-config>
	<security-role>
		<role-name>admin</role-name>
	</security-role>
	<security-role>
		<role-name>user</role-name>
	</security-role>

エラーが出るとは思うけど、このままでJetty起動してみる。
java.lang.IllegalStateException: No LoginService
出ました。設定せねば。

次、http://wiki.eclipse.org/Jetty/Tutorial/Realms
みると、
全てのアプリケーションに同一の設定をする場合は、jetty.xml
アプリケーション個別に設定する場合は、context.xml
にそれぞれ、LoginServiceの設定が出来ますよっと書いてある。
開発環境では、context.xmlを参照せず、Jettyを起動していて、
本番環境では、context.xmlを設定しています。
開発環境でcontext.xmlを参照して起動する方法がわかってないだけなんですが、
まぁ、複数アプリケーションを動かす予定はないので、jetty.xmlに設定します。
jetty.xml

    <!-- =========================================================== -->
    <!-- Set Realms                                                  --> 
    <!-- =========================================================== -->
    <Call name="addBean">
      <Arg>
        <New class="org.eclipse.jetty.security.HashLoginService">
          <Set name="name">hogeAuth</Set>
          <Set name="config"><SystemProperty name="jetty.home" default="."/>/etc/realm.properties</Set>
          <Set name="refreshInterval">0</Set>
        </New>
      </Arg>
    </Call>

また、エラーが出るとは思うけど、このままでJetty起動してみる。
java.lang.IllegalStateException: No LoginService
同じエラー。ほぅ・・・。
前に、TomcatからJetty(http://d.hatena.ne.jp/nosa1/20120124/1327397386)
の設定をしましたが、Jettyのデバッグの設定、これやっぱりrun-jetty-runが内包してるJetty
をみてるっぽいです。
だもんで、デバッグの構成の一番下にある「Additional Jetty.xml」に
編集した単体Jettyのjetty.xmlを設定します。もう一回起動。

:FAILED HashLoginService[hogeAuth]: java.io.FileNotFoundException:
・・・省略。
\etc\realm.properties (指定されたパスが見つかりません。)

予想通り。まだ無いからね(実はあります)。
あら??/etcの上のディレクトリが、eclipseのプロジェクト名になってる。
run-jetty-runを使ってますが、これで起動すると、
jetty.homeがデバッグの構成で割り当てたプロジェクトになるっぽい。

困った。jetty.xmlは、単体Jettyのを参照するように出来るけど、
realm.propertiesは、eclipseワークスペースに入れなきゃいけないなんて。
そうすると、単体Jettyを起動すると、jetty.homeが単体Jettyのディレクトリになるから、
このままだと、単体Jettyのetcにrealm.properties、ワークスペースのetcにもrealm.properties
とrealm.propertiesを2つもつハメになってしまう。
run-jetty-runで、jetty.homeが設定できればいいんですけど。
Tomcatプラグインだと、TomcatHomeが設定出来たんだけどなー。
あまり調べきれていませんが、
起動オプションでjetty.homeを変更する方法は、見つけられませんでした。
また、jetty.xmlにjetty.homeを記述する方法も分かりませんでした。
http://wiki.eclipse.org/Jetty/Tutorial/Embedding_Jetty#Configuring_Connectors
これとか見ると、なんとなく出来そうなんですけどね。
起動したら、そこがHomeって事なんでしょうか・・・。
で、色々設定みてたのですが、やっぱrun-jetty-runにココがhomeだよって教えてあげなきゃいけないわけで。
とりあえずの解決ですが、
デバッグの構成から、引数のタブ、作業ディレクトリーを単体Jettyのディレクトリにしてあげると
ここをjetty.homeと認識するかは不明ですが、単体Jettyの/etcを参照するようです。
起動時にエラーがなくなったので、ブラウザアクセスしてみる。
404!
なんで???ここは401でしょー!
認証の設定外してみる。げっ、404・・・。
これ、デバッグの構成で、「Additional Jetty.xml」に設定すると、その上にあるプロジェクトの設定無視しますね・・・。
(作業ディレクトリをワークスペースに戻して404が出るし)
つーことは、単体Jettyのjetty.xmlにコンテキストの設定しないとってことか。
https://github.com/alexwinston/RunJettyRun
ここ参考に設定しました。
jetty.xml

    <!-- =========================================================== -->
    <!-- Set handler Collection Structure                            --> 
    <!-- =========================================================== -->
    <Set name="handler">
      <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
        <Set name="handlers">
         <Array type="org.eclipse.jetty.server.Handler">
           <!-- ======================================================= -->
           <!-- Configure a web application with web.xml -->
           <!-- ======================================================= -->
           <Item>
               <New class="org.eclipse.jetty.webapp.WebAppContext">
                   <Set name="contextPath">/hoge</Set>
                   <Set name="resourceBase">C:\workspace\webapp</Set>
               </New>
           </Item>
           <Item>
             <New id="Contexts" class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
           </Item>
           <Item>
             <New id="DefaultHandler" class="org.eclipse.jetty.server.handler.DefaultHandler"/>
           </Item>
         </Array>
        </Set>
      </New>
    </Set>

デバッグも大丈夫。作業ディレクトリを単体Jettyにしても大丈夫。
棚ぼたというかなんというか、jetty.xmlにコンテキストの設定をしてしまったので、
context.xml要らなくなりました。まぁ、勉強になったってことで・・・。
認証の設定戻す→ブラウザアクセス→認証のダイアログでる→401!

次は、realm.propertiesを設定します。
デフォルトだと、realm.propertiesって、既にありました。
サンプルとして持っておきたいので、別で作成します。
realm.properties

admin: MD5:164c88b302622e17050af52c89945d44,admin
user: MD5:164c88b302622e17050af52c89945d44,user

ブラウザからアクセスしてみると、認証ポップアップが出る。
ユーザ名とパスワードを入れる。認証通らない・・・。
BASIC認証に変えてみる。認証通る。うーん。
と悩んでたら、サンプルのrealm.propertiesに、
「If DIGEST Authentication is used, the password must be in a recoverable
format, either plain text or OBF:.」
DIGEST認証使う場合は、プレーンテキストかOBF形式使いなさいと書いてある。
ほぅ。設定にプレーンテキストは書きたくないので。OBF使います。
すいません。ところでOBFってなんですか?
http://d.hatena.ne.jp/yukinkster/20110828/1314563738
ここ参考にさせて頂きました。(SSLの時もお世話になりそうです)
本家のドキュメントは、これhttp://wiki.eclipse.org/Jetty/Tutorial/Passwords
が、
8.1.0.RCを使っているからか、ドキュメント通りでは、動きませんでした。

C:\jetty\jetty-distribution-8.1.0.RC1\lib>java -cp jetty-util-8.1.0.RC1.jar org.eclipse.jetty.util.security.Password me password
password
OBF:1v2j1uum1xtv1zej1zer1xtn1uvk1v1v
MD5:5f4dcc3b5aa765d61d8327deb882cf99
CRYPT:me797W335G/ME

ドキュメントと違うのは、httpのjar必要ない。
jetty-util-8.1.0.RC1.jarの中を見てみたら、パッケージ構成がドキュメントと違う。
これでOBFが手に入りました。これをrealm.propertiesに設定します。
で、ブラウザアクセス。認証OK!

さて、アプリケーション側変更します。Flex(AIR)です。
幸いAIRなので、楽チンです。Flexアプリケーションの場合は、ちょっと面倒です。
こことか http://livedocs.adobe.com/flex/3/html/help.html?content=url_requests_2.html
URLRequestDefaultsのAPI http://livedocs.adobe.com/flex/3_jp/langref/flash/net/URLRequestDefaults.html
見ると分かります。

URLRequestDefaults.authenticate = true;
URLRequestDefaults.useCache = false;
URLRequestDefaults.setLoginCredentialsForHost("www.example.com", "Ada", "love1816$X");

マニュアル通りです。URLVariables忘れずに。
と思ったら、罠がありました。
http://livedocs.adobe.com/flex/3_jp/langref/flash/net/URLRequestDefaults.html#setLoginCredentialsForHost()
「メモ : このメソッドは、ファイルのアップロードまたは RTMP 要求で使用される URLRequest オブジェクトには適用されません。」
うおーぃ。ファイルアップロード使ってるよー。
確かにファイルアップロード、エラリます。
ついでに、
http://www.riaxdnp.jp/?p=908
こんな素敵なご指摘もありますね。

整理すると、
setLoginCredentialsForHost()は、ファイルアップロードでは使えない(適用されない)

URLRequestで何とかする必要がある。

FileReferenceのupload
APIリファレンス(http://livedocs.adobe.com/flex/3_jp/langref/flash/net/FileReference.html#upload())
「URLRequest オブジェクトの requestHeaders プロパティは無視されるため、カスタム HTTP リクエストヘッダはアップロードまたはダウンロードでサポートされません。」
とあるので、URLRequestHeaderは使えない。

降参?
ネット見てると、MacではNGってのは通説っぽいけど、Winだといけるってありますね。
ヘッダ使えないのに、どうやってるんだろ?
これか、http://unknownplace.org/memo/2007/10/26/#e001
やってるっていうか、やってくれちゃってるんですね。
さーて、どうしよっかなー。
まず、テキスト送信出来るケースは、普通にPOSTしよう。今日はここまで。