スプリングセキュリティを ZK で動作させる
From Documentation
- Author
- Henri Chen, Principal Engineer, Potix Corporation
- Date
- September 15, 2008
- Version
- Applicable to ZK 3.0.8 and later.
- Applicable to ZK 3.5.0 and later.
- Applicable to Spring Security 2.0+
イントロダクション
Spring Security 2.0 はスプリングフレームワークに於ける次世代セキュリティシステムです。これには以前の Acegi セキュリティシステムから沢山の新規機能が盛り込まれています。スプリングセキュリティ2.0を ZK Ajax フレームワークで動作させることについては質問が寄せられていました。ここにそのサンプルのデモをお見せすると共に、如何にして動作させるかをステップバイステップで解説します。
デモ
サンプル
シンプルに事を進めるために、スプリングセキュリティ2.0 の供給するチュートリアル(spring-security-2.0.3/dist/spring-security-samples-tutorial-2.0.x.war) を借りることにしました。 その後、オリジナルのピュア JSP ページを ZK ページに書き直して、必要な ZK ライブラリーを追加してこれらの2つのフレームワークが協調してシームレスに動作する事が可能なことを見せるための構成をしました。このチュートリアルサンプルは remember-me と注釈メソッド認証その他を含むフォームベース認証メカニズムセキュリティシステムの多くの基本的なコンセプト見せてくれます。この記事ではスプリングセキュリティメカニズムの詳細に入るつもりはありません。むしろ「まず動作させる」ことに主眼を置き、ZK ページで表示します。背景の技術に興味がありましたら、スプリングセキュリティの Web サイトで確認したください。
War ファイルのデプロイ
最も容易にサンプルを確認する方法は、サーブレットコンテナに War ファイルをデプロイすることです。ここでは Tomcat 5.5 を使用します。とても簡単です。 spring-security-samples-tutorial-2.0.x.war ファイルを Tomcat サーバーの webapps フォルダにコピーして Tomcat をリスタートするだけです。Tutorial war ファイルは webapps の下に新たに spring-security-samples-tutorial-2.0.x(ファイルと同名) フォルダーを作成します。そしてブラウザで http://localhost:8080/spring-security-samples-tutorial-2.0.x/ を見ると、次のように見えます。
/WEB-INF/web.xml ファイルの構成
スプリングセキュリティが ZK Web アプリケーションと共に動作するにはどうするかを、構成ファイルを「つぶさ」に見ることから始めることにします。/WEB-INF/web.xml が Tomcat に知らせるための主要な構成ファイルです。Tomcat がスプリングセキュリティと共に動作するには、以下の様に構成します。これらは既にチュートリアルサンプルの中で設定済みなので、基本的にはあまり修正するところはありません。
/WEB-INF/web.xml ... <!-- - Location of the XML file that defines the root application context - Applied by ContextLoaderListener. --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> ... /WEB-INF/applicationContext-security.xml </param-value> </context-param> <!-- - Loads the root application context of this web app at startup. - The application context is then available via - WebApplicationContextUtils.getWebApplicationContext(servletContext). --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- - Spring Security Filter Chains --> <filter> <filter-name>springSecurityFilterChain</filter-name> <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> </filter> <filter-mapping> <filter-name>springSecurityFilterChain</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> ... <!-- - ZK configurations --> <listener> <description>Used to cleanup when a session is destroyed</description> <display-name>ZK Session Cleaner</display-name> <listener-class>org.zkoss.zk.ui.http.HttpSessionListener</listener-class> </listener> <servlet> <description>ZK loader for evaluating ZK pages</description> <servlet-name>zkLoader</servlet-name> <servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class> <init-param> <param-name>update-uri</param-name> <param-value>/zkau</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>zkLoader</servlet-name> <url-pattern>*.zul</url-pattern> </servlet-mapping> <servlet> <description>The asynchronous update engine for ZK</description> <servlet-name>auEngine</servlet-name> <servlet-class>org.zkoss.zk.au.http.DHtmlUpdateServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>auEngine</servlet-name> <url-pattern>/zkau/*</url-pattern> </servlet-mapping> ... <welcome-file-list> index.zul </welcome-file-list>
- スプリング セキュリティ 構成
- <context-param> はスプリングフレームワークのコンテクストローダーにコンテクストパラメータファイルを何処で探せば良いか知らせます。
- <listener> ContextLoaderListener はスプリングフレームワークのコンテクストローダーを定義していて、コンテクストパラメータをロードしてパースします。
- <filter> springSecurityFilterChain はスプリング゛セキュリティフィルターチェーンのサーブレットフィルターの入り口です。
- <filter-mapping> は(URL パターンに従って)どのページを通過し、どのページをスプリングセキュリティフィルターチェーンを使ってチェックするかを定義します。
スプリングセキュリティに特有の構成は実に最後の 2 項目(項目 3と4)です。 springSecurityFilterChain のフィルター名称は 重要です。これらはそのとおりコピーしてください。項目 1 と 2 はスプリングフレームワークを動作させるための汎用的な構成です。項目 1 内で設定されている /WEB-INF/applicationContext-security.xml はスプリングセキュリティ特有の構成ファイルで、この中に他のビジネス仕様のスプリング構成を含むこともできます。このことについては後から述べます。
- ZK Ajax フレームワークの構成
- <listener> HttpSessionListener はセッションが消滅したとき、掃除するために使用します。
- <servlet> zkLoader servlet は ZK ページをロードするのに使用します。
- <servlet> auEngine servlet は ZK ページを更新するのに使用します。 (Ajax 更新)
- zkLoader の <servlet-mapping> は *.zul ページに指定されており、全ての Ajax XMLHttpRequest の url は /zkau/* パターンから始まります。
ライブラリファイルを /WEB-INF/lib にコピーする
/WEB-INF/web.xml が設定されたので、その後は必要とされるライブラリです。スプリングセキュリティが動作するためには次の jar を /WEB-INF/lib. にコピーします。
Spring Security library jar files aopalliance-1.0.jar aspectjrt-1.5.4.jar commons-codec-1.3.jar commons-collections-3.2.jar commons-lang.jar commons-logging-1.1.1.jar jstl-1.1.2.jar log4j-1.2.14.jar spring-security-acl-2.0.3.jar spring-security-core-2.0.3.jar spring-security-core-tiger-2.0.3.jar spring-security-taglibs-2.0.3.jar spring.jar *spring-web.jar *spring-webmvc.jar standard-1.1.2.jar *これらの2つの jar ファイルは必要はないのですが、このサンプルで使われているので含まれています。
そして次は ZK Quick Start Guide で見るように、ZK フレームワークで使用される基本的な ライブラリです。全ての /dist/lib/*.jar, /dist/lib/ext/*.jar, /dist/lib/zkforge/*.jar を /WEB-INF/lib にコピーしてください。
ZK library jar files bsh.jar commons-collections.jar commons-fileupload.jar commons-io.jar commons-logging.jar fckez.jar Filters.jar gmapsz.jar groovy.jar itext.jar jasperreports.jar jcommon.jar jfreechart.jar jruby.jar js.jar jxl.jar jython.jar mvel.jar ognl.jar poi.jar timelinez.jar zcommon.jar zcommons-el.jar zhtml.jar zk.jar zkex.jar zkmax.jar zkplus.jar zml.jar zul.jar *zuljsp.jar zweb.jar *これは ZK JSP タグライブラリーです。このサンプルの中で使用しています。
/WEB-INF/zk.xml ファイルの構成とThreadLocal 問題(重要)
スプリングセキュリティエンジンはサーブレットスレッドの中に ThreadLocal variable である、contextHolder を各々のリクエスト毎に保持しているので、エンジンはその時、その時の情報を参照できます。 ThreadLocal variable はセキュリティ関連の重要な情報を持っているので、何時でもアクセスできるべきです。しかしながら、ZK はデフォルトで各々のイベントハンドリングジョブで新規のイベントスレッドを起こします。すなわち、 ZK イベントスレッドはそのような重要な contextHolder という ThreadLocal variable を持ちません。で、オリジナルの想定は破られます。
この問題を解決するには2つの方法があります。これらのどちらかを選択して/WEB-INF/zk.xml ファイルに設定します。
- 1. ZK イベントスレッドメカニズムを Disable にします。これにより、ZK フレームワークは新規のイベントスレッドを起こさなくなるので、全てがノーマルにもどります。
<system-config> <disable-event-thread/> </system-config>
- 2. ZK が持つ ThreadLocalListener ユーティリティを使います。それによりcontextHolder という ThreadLocal variable をサーブレットスレッドから ZK イベントスレッドにコピーします。そして逆もあります。
<listener> <description>ThreadLocal Synchronization Listener</description> <listener-class>org.zkoss.zkplus.util.ThreadLocalListener</listener-class> </listener> <preference> <name>ThreadLocal</name> <value> org.springframework.security.context.ThreadLocalSecurityContextHolderStrategy=contextHolder </value> </preference>
ZK イベントスレッドメカニズムが Messagebox のような ZK modal window の基本であることを注目してください。すなわち、項目1案のようにイベントスレッドメカニズムを disable すると、ZK modal window を使用できなくなります。どちらの解決策をとるかは要求条件によります。
/WEB-INF/applicationContext-security.xml の構成
このファイルはスプリングセキュリティエンジンに何をするかを指示します。このファイルがおそらくスプリングセキュリティシステムの最も重要なものです。このファイルは新規の簡潔なネームスペース-ベースのシンタックス構成を使用しており、古い構成方法では数百行にも及ぶコードを僅か10行に圧縮します。
/WEB-INF/applicationContext-security.xml <!-- - Spring namespace-based configuration --> <beans:beans xmlns="http://www.springframework.org/schema/security" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd"> <!-- - Enable the @Secured annotation to secure service layer methods --> <global-method-security secured-annotations="enabled"> </global-method-security> <!-- - Secure the page per the URL pattern --> <http auto-config="true"> <intercept-url pattern="/secure/extreme/**" access="ROLE_SUPERVISOR"/> <intercept-url pattern="/secure/**" access="IS_AUTHENTICATED_REMEMBERED" /> <intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" /> <!-- use own login page rather than the default one --> <form-login login-page="/login.zul"/> </http> <!-- Usernames/Passwords are rod/koala dianne/emu scott/wombat peter/opal --> <authentication-provider> <password-encoder hash="md5"/> <user-service> <user name="rod" password="a564de63c2d0da68cf47586ee05984d7" authorities="ROLE_SUPERVISOR, ROLE_USER, ROLE_TELLER" /> <user name="dianne" password="65d15fe9156f9c4bbffd98085992a44e" authorities="ROLE_USER,ROLE_TELLER" /> <user name="scott" password="2b58af6dddbd072ed27ffc86725d7d3a" authorities="ROLE_USER" /> <user name="peter" password="22b5c9accc6e1ba628cedc63a72d57f8" authorities="ROLE_USER" /> </user-service> </authentication-provider> </beans:beans>
- 1. <global-method-security secured-annotations="enabled"> はスプリングセキュリティエンジンに @Secured という Java アノテーションを使用して、サービスレイヤーのメソッドをセキュアにすることを指示します。
- 2. <http auto-config="true"> はスプリングセキュリティエンジンにデフォルト構成のセキュリティフィルターチェーンとサービスを使用することを指示します。この単一の行で構成の90%をカバーします。
- <intercept-url pattern="..." access="..."> は、(URL パターンに従って)どのロールのユーザーがどのページにアクセス出来るかを指示します。
- <form-login login-page="/login.zul"/> はユーザーがログインする際のフォームページとして、/login.zul を使うことを指示します。これを指定しないときは、セキュリティシステムが内部の1つを自動的に使用します。
- 3. <authentication-provider> は内部メモリーの user/password リストです。これはテストや単純な場合に使用します。要求条件によっては、独自の user/password プロバイダメカニズム(たとえばデータベースを使用するなど)を実装することも可能です。ロングインテジャーの password="xxxxxxxxxxx..."はパスワード文字列の md5 結果で、インターネット上でプレーンテキストが渡されるのを防止します。
ここまでで、構成要領については終了です。この後はオリジナルの JSP ページを ZK ページに書き換えて ZK とスプリングセキュリティが共に動作できることをお見せします。
/index.jsp を /index.zul へ書換え
これは容易い作業です。スクリーンに <grid> コンポーネントを置き、<button href="..."> を他のページへのハイパーリンクとして使用します。オリジナルの index.jsp と比較してみてください。
/index.zul ... <grid> ... <rows> <row> Any one can list accounts.<button label="Go!" href="listAccounts.html"/> </row> <row> Your principal object is ...:<label value="${desktop.execution.userPrincipal.name}"/> </row> <row> Secure page<button label="Go!" href="secure/secure.jsp"/> </row> <row> Extremely secure page<button label="Go!" href="secure/extreme/extreme.jsp"/> </row> </rows> </grid> ...
このページはこのサンプルの入り口のページです。最初の行のボタンは全ての account のリストを表示します。2番目のページは現在のログインユーザー名(ログインしていなければブランク)を表示します。3番目の行のボタンを押すとセキュアなページにアクセスします。最後のボタンは最高にセキュアなページで ROLE_SUPERVISOR の許可が必要です。
/login.jsp を /login.zul へ書換え
これはカスタマイズされた login ページです。必要になったとき、スプリングセキュリティはこの login ページを表示します。再び、このスクリーンを grid と ZK コンポーネントと html ( <h:form> , <h:input ...> ) とのミックスでレイアウトしました。これは、 ZK コンポーネントと従来のサーブレットとフォームーベースのページとのミックスをする典型的なものです。詳細についてはこの article を参照してください。通常のログインページは "Valid users" の部分はありません。
これはチュートリアルサンプルですので・・・このサンプルではユーザーの rod は ROLE_SUPERVISOR の許可を持っており、dianne は
ROLE_TELLER の許可を持っていて、他の二人は ROLE_USER の許可を持っています。詳細は
/WEB-INF/applicationContext-security.xml をチェックしてください。このサンプルをチェックして各々が各ページへのアクセスの仕方を見てください。
/login.zul ... <h:form id="f" name="f" action="j_spring_security_check" method="POST" xmlns:h="http://www.w3.org/1999/xhtml"> <grid> <rows> <row>User: <textbox id="u" name="j_username"/></row> <row>Password: <textbox id="p" type="password" name="j_password"/></row> <row> <checkbox id="r" name="_spring_security_remember_me"/> Don't ask for my password for two weeks </row> <row spans="2"><hbox> <h:input type="submit" value="Submit Query"/> <h:input type="reset" value="Reset"/> </hbox></row> </rows> </grid> </h:form> ...
/WEB-INF/jsp/listAccounts.jsp を /WEB-INF/jsp/listAccounts.zul へ書換え
このページは全ての account のリストを表示するのに使われます。ホームページ(/index.zul) の最初のボタンを押すとこれが表示されます。注意深く見ると、見ている URL は /listAccounts.html である事に気づきます。どうして、root の .html ページを見ているのに、/WEB-INF/jsp/ 内の .zul ページの表示を見ることになるのでしょうか。これはスプリングMVCに内蔵されたフォーワードメカニズムに拠るものです。興味があるなら、 /WEB-INF/bank-servlet.xml, /src/bigbank/web/ListAccounts.java, と /src/bigbank/BankService.java などのソースファイルをチェックしてみてください。これはスプリングセキュリティシステムと直接関連することではないのですが、少し手短に解説します。
- /listAccounts.html へのリクエストが発生します。
- /WEB-INF/bank-servlet.xml 内の定義により ListAccounts コントローラの handleRequest メソッドがコールされます。
- そしてこのメソッド内で bankService.findAccounts() がコールされて accounts 変数がインスタンス化されます。ここではそれをIS_AUTHENTICATED_ANONYMOUSLY に設定しますので、誰でもこのメソッドにアクセスできます。
- bankService.findAccounts() ではスプリングセキュリティアノテーション @Secured を使用して、メソッドコールをセキュアにできます。
- そして url は /WEB-INF/bank-servlet.xml 内のパターン定義により /WEB-INF/jsp/listAccounts.zul にフォワードされます。
listAccounts.zul に於いては、 accounts コレクションに繰り返し処理手続きをすることで <grid> 内の 行で各々の Account を見ることが出来ます。
/WEB-INF/jsp/listAccounts.zul ... <grid> <rows> <row forEach="${accounts}"> <label value="${each.id}"/> <label value="${each.holder}"/> <label value="${each.balance}"/> <button label="-$20" href="post.html?id=${each.id}&amount=-20.00"/> <button label="-$5" href="post.html?id=${each.id}&amount=-5.00"/> <button label="+$5" href="post.html?id=${each.id}&amount=5.00"/> <button label="+$20" href="post.html?id=${each.id}&amount=20.00"/> </row> </rows> </grid> ...
このページでユーザーは "-$20", "-$5", "+$5", "+$20" ボタンを押して各 Account のトータル金額を 増/減 出来、操作後自動的に表示が更新されます。どのようにしてこのような動作が起きるのでしょうか? <button ... href="post.html?id=..."/> のコードを見てください。これもスプリングMVCが絡んでいるところです。興味があるなら、 /WEB-INF/bank-servlet.xml, /src/bigbank/web/ListAccounts.java, と /src/bigbank/BankService.java などのソースファイルをチェックしてください。
- ボタンがクリックされると、"post.html?id=..." のリクエストが発生します。
- /WEB-INF/bank-servlet.xml 内の定義により PostAccounts コントローラの handleRequest メソッドがコールされます。
- そしてこのメソッド内で bankService.readAccount() がコールされて bankService.post() が account の amount を修正します。
- bankService.readAccount() と bankServer.post() でスプリングセキュリティアノテーションの @Secured を使用して、これらの2つのメソッドコールをセキュアにします。ここでは、 bankServer.post() は ROLE_TELLER 許可により保護されています。このことは ROLE_TELLER 許可を持つユーザーだけがこのメソッドを使用できることを意味します。
- そして、 PostAccounts コントローラ内の handleRequest メソッドのリクエストで /listAccounts.html の URL にリダイレクト戻しされます。
- それで /listAccount.html はここまで説明したように表示が更新されます。
/secure/index.jsp の書換え
/secure/index.jsp <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <%@ taglib prefix="z" uri="http://www.zkoss.org/jsp/zul" %> <%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags" %> <html> <body> <z:page> <z:window title="Secure Page" border="normal" width="500px"> <p> This is a protected page. You can get to me if you've been remembered, or if you've authenticated this session. </p> <sec:authorize ifAllGranted="ROLE_SUPERVISOR"> You are a supervisor! You can therefore see the <a href="extreme/index.jsp">extremely secure page</a>.<br/><br/> </sec:authorize> <h4>Properties obtained using <sec:authentication /> tag</h4> <z:grid> <z:columns> <z:column label="Tag"/> <z:column label="Value" width="50px"/> </z:columns> <z:rows> <z:row><z:label value="<sec:authentication property='name' />"/><sec:authentication property="name"/></z:row> <z:row><z:label value="<sec:authentication property='principal.username' />"/><sec:authentication property="principal.username"/></z:row> <z:row><z:label value="<sec:authentication property='principal.enabled' />"/><sec:authentication property="principal.enabled"/></z:row> <z:row><z:label value="<sec:authentication property='principal.accountNonLocked' />"/><sec:authentication property="principal.accountNonLocked"/></z:row> </z:rows> </z:grid> <z:separator bar="true"></z:separator> <z:button label="Home" href="../"/> <z:button label="Logout" href="../j_spring_security_logout"/> </z:window> </z:page> </body> </html>
このページは少し特殊です。オリジナルの JSP ではこのページの一部の可視性を制御するためにスプリングセキュリティの <sec:...> タグを使用してセキュリティ関連の情報をフェッチしています。ここでは ZK コンポーネントを使用しながら、これらの <sec:...> 機能を保たねばなりません。これをどのようにして実現するのでしょうか? このような場合に対処するための ZK JSP タグ を使用します。 ZK JSP タグは従来の JSP ページを直接的な方法で手を加えて、シームレスに JSP タグと統合して解決します。
- <!DOCTYPE ...> はインターネットエクスプローラでは重要なので、そのままコピーします。
- <%@ taglib prefix="z" uri="http://www.zkoss.org/jsp/zul" %> は ZK JSP タグの宣言なので、一般的にはプレフィックスに "z" を付加します。
- ZK <z:...> 全てのその他のコンポーネントはタグは <z:page>タグで囲みます。
- そうすることで、その他の JSP タグと HTML タグを問題なく内部にミックスできます。
ご覧のとおり、たぶんこれが JSP ページに修正を加える最も容易な方法でしょう。
スプリングセキュリティそのものに戻ります。 <sec:authorize ifAllGranted="ROLE_SUPERVISOR"> タグはスプリングセキュリティエンジンに user が ROLE_SUPERVISOR の許可で承認されるまで"You are a supervisor! ..." のコンテンツは表示されません。 <sec:authentication property="principal.username"> は CURRENT のログインユーザー名をフェッチします。他にも有用なセキュリティタグが有ります。詳細はスプリングセキュリティWebサイトをご覧ください。
/secure/extreme/index.jsp の書換え
このページは保護されていますので、 ROLE_SUPERVISOR 許可が許されているユーザーのみがアクセスできます。 /secure/index.jsp が使っているのと同様にスプリングセキュリティの <sec:...> タグを使用しています。再び、 ZK コンポーネントと一緒に使用するために ZK JSP タグで修飾します。
/secure/extreme/index.jsp <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <%@ taglib prefix="z" uri="http://www.zkoss.org/jsp/zul" %> <%@ taglib prefix="authz" uri="http://www.springframework.org/security/tags" %> <html> <body> <z:page> <z:window title="VERY Secure Page" border="normal" width="500px"> This is a protected page. You can only see me if you are a supervisor. <authz:authorize ifAllGranted="ROLE_SUPERVISOR"> You have "ROLE_SUPERVISOR" (this text is surrounded by <authz:authorize> tags). </authz:authorize> <z:separator bar="true" spacing="10px"/> <z:hbox> <z:button label="Home" href="../../"/> <z:button label="Logout" href="../../j_spring_security_logout"/> </z:hbox> </z:window> </z:page> </body> </html>
サマリー
スプリングセキュリティ2.0は Web アプリケーションにとって強力なセキュリティシステムです。これは URL ページとサービスレイヤーメソッドコールをシンプルな構成とアノテーションでセキュアにできますし、 ZK JSP タグは ZK タグとスプリングセキュリティタグをシームレスにミックスできる簡単な方法を提供します。
この記事では、従来のページ-ベースの Web アプリケーションについて論じましたが。このことは Ajax のようなアプリケーションを扱えないということを意味しません。たとえば、この記事のページの途中で、ユーザーがログインすを要求される途中の段階まで来たとき、別のログインページを使うことで現在見ているページから離れることを強制されるよりも、ポップアップウィンドウでログインできたほうが良いでしょう。このようなインタラクティブで、キビキビとした方法で認証やセキュリティを扱うには ZK フレームワークとスプリングセキュリティシステムがより協調する必要があります。このことについてはこのトピックについての次回の記事で取り扱います。
ダウンロード
- サンプルコード(.war file)をダウンロードできます。
Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License. |