Java Springの逆引きメモ -9ページ目

Java Springの逆引きメモ

JavaのSpring frameworkのメモを書いていきます!
初心者の勉強ノートなので間違いがあるかもしれませんが、何かヒントになることがあれば幸いです。

'2012.03.23 追記

このブログで扱っているSpringMVCのバージョンは2.5で、情報が古くなっています。

現状、アノテーションを使用してかなり柔軟で使いやすくなっていますにひひ

この記事を削除することはしませんが、最新の情報を検索されることをお勧めします。

Spring MVC 3.xについて(日本語の記事)



-------------------------

さて、ここでは基本的なSpring MVCの使用方法を見てみましょう!


もしSpringのweb.xmlなどの基本的な設定方法が分からない場合は以下を参照してください。

パラメタの文字コードの設定もしています。


 ・SpringでWEB作成するには?(基本的な作成例)



SpringMVCのControllerは複数存在します。UrlFilenameViewControllerは上の記事で使用しているので、ここではAbstractCommandControllerを継承して自作のControllerを作ります。


※通常Spring MVCでWEBを作る場合、SimpleFormControllerクラスを継承します。

下記の例はSimpleFormControllerを使用すればコードを書く必要はないのですが、

コードを書く必要がないということは動きが見えにくくなるかと思います。

それで記事として、原始人のようなAbstractCommandControllerクラスを使用することにしました。


【Controllerクラス】

package presentation;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.validation.BindException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractCommandController;

import business.domain.CmdTest;

public class CmdTestController extends AbstractCommandController {
 
 @Override
 protected ModelAndView handle(HttpServletRequest req,
   HttpServletResponse res, Object obj, BindException bind)
   throws Exception {
  
  //モデルクラス(データクラス)
  CmdTest cmd = (CmdTest)obj;

  //ビューとデータ
  ModelAndView modelAndView = new ModelAndView("/WEB-INF/jsp/cmdTest.jsp");
  modelAndView.addObject("cmd", cmd);
  return modelAndView;
 }
 
}





【モデル】

モデルクラス(POJO)です。

WEBから受け取ったパラメタの値を代入します。

StrutsでいうとActionFormに当たりますが、StrutsのようにActionFormを継承する必要がなく、フレームワークにデータクラスがが汚されないのが特徴です。

AbstractCommandControllerが画面のパラメタから自動でこのモデルクラスを作成してくれます。

とっても便利。

このあたりもStrutsのActionFormと同じです。ただしSpringの場合、パラメタとActionFormの関係をわざわざ設定ファイルに記述する必要がありません。


package business.domain;
public class CmdTest {
 private boolean display = false;
 private String message;
 
 public boolean isDisplay() {
  return display;
 }
 public void setDisplay(boolean display) {
  this.display = display;
 }
 public String getMessage() {
  return message;
 }
 public void setMessage(String message) {
  this.message = message;
 }
 
}





【test-servlet.xmlの設定ファイル】

Springの設定ファイルです。

Controllerの設定とは別の設定も一応記述しています。

今回はControllerの設定のところだけ確認してもらえればと思います。



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans "
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance "
xmlns:aop="http://www.springframework.org/schema/aop "
xmlns:tx="http://www.springframework.org/schema/tx "
xmlns:util="http://www.springframework.org/schema/util "
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd ">


<!--
 メッセージリソースの設定。
 bean名はmessageSourceにする必要があります。
-->

<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>


<!-- HandlerMapping -->
<bean id="handlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/input.html">fileNameController</prop>
<prop key="/cmdTest.html">cmdTestController</prop>
</props>
</property>
</bean>



<!-- JSP表示用のController
コントローラは、xxx-servlet.xmlに記述しないと、メッセージリソースのLocaleが設定されない。
つまり、日本語と判定してくれず、messages_ja.propertiesファイルを参照してくれない。
-->

<bean id="fileNameController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>

<!-- 継承した自作Controller -->

<bean id="cmdTestController" class="presentation.CmdTestController">
<property name="commandClass" value="business.domain.CmdTest" />
</bean>


</beans>



【JSP入力ファイル(/WEB-INF/jsp/input.jsp)】


<%@ page language="java" pageEncoding="utf-8" contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"% >

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd ">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>入力テスト</title>
</head>
<body>
CmdControllerテスト<br>
<form action="cmdTest.html" method="post">
<input type="checkbox" name="display" value="true">メッセージを表示します<br>
<input type="text" name="message">
<input type="submit" value="入力">
</form>
</body>
</html>




【JSP結果ファイル(/WEB-INF/jsp/cmdTest.jsp)】


<%@ page language="java" pageEncoding="utf-8" contentType="text/html; charset=utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"% >

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd ">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>入力テスト</title>
</head>
<body>
CmdControllerテスト<br>
表示がTRUEのときにメッセージを表示します。<br>
<br>
<c:if test="${cmd.display}">
メッセージ:<c:out value="${cmd.message}"/>
</c:if>
</body>
</html>




【画面】

画面を表示するには、以下のURLをアクセスします。

プロジェクト名などが違う場合は、合わせてtestの部分を書き換えてアクセスしてください。

http://localhost:8080/test/input.html



<入力画面>
 Java Springの逆引きメモ
    ↓


 <結果画面>
 Java Springの逆引きメモ





【説明】

動作としては、チェックボックスをチェックするとテキストに入力した値を表示するという簡単なものです。


見るべきところは、入力画面からモデルオブジェクトに値を入れるところです。


まずtest-servlet.xmlファイルの以下の場所を見てください。

<bean id="cmdTestController" class="presentation.CmdTestController">
<property name="commandClass" value="business.domain.CmdTest" />
</bean>

commandClassプロパティに、モデルのクラス名を設定しています。

これはAbstractCommandControllerが持っているプロパティと機能です。

ここで設定したクラスを生成してパラメタの値を設定します。

ただし、パラメタ名とモデル(CmdTest)のクラスのgetter名はあわせておく必要があります。


次に見るべきは、CmdTestController クラスです。

handleメソッドを見てください。

Objectが引数になっています。

これが生成されたモデルクラスです!


 //モデルクラス(データクラス)
 CmdTest cmd = (CmdTest)obj;

のようにキャストするだけでモデルを使用することができます。

簡単ですよね!





【Spring MVCの使い方】

使い方の基本は以上のとおりです。

つまり、以下の感じです。


 ①Springで用意しているControllerクラスをそのまま使用する。

  (上記では、UrlFilenameViewControllerクラス)


 ②Springで用意しているControllerクラスを継承して使用する。

  (上記では、CmdTestController


 ③SpringのControllerインターフェースをimplementsして完全に自作する。

  ここでは紹介していませんし、めったにはやらないのですが完全自作してもよいのです!



Strutsでは、ActionやActionFormをextendsしないといけない制約があります。

Javaでは複数のクラスをextendsすることができないのでこれは再利用性などを考えるとそこそこ強い制約です。

Springではそのようなことがないのが特徴です。



いいですよね!Spring!



【補足】

ここでは妥当性チェック機能はつけていません。

これもStrutsと同じように設定ファイルに記述するだけで可能になります。

興味のある方は、別の記事に記載予定ですので、それを参照ください。


また、今回はパラメタとモデルを結びつけましたが、実はDate型はそのままでは結び付けられません。

結びつけの方法は別の記事で見ていこうと思います。





参考:

・トップ

・SpringMVCを使用してWEBを作るには? (実践編)

・SpringMVCの機能について

・Controller内でApplicationContextを取得するには?

・リクエストの文字コードを設定ファイルで制御するには?



ここでは、よく必要になるSpringのファイルと、それをどこに置くのかを記述してみます。


xxxはプロジェクト名を表しています

ファイル名 パス 概要
applicationContext.xml xxx\WebContent\WEB-INF Springの設定ファイル。必須です。
xxx-servlet.xml

xxx\WebContent\WEB-INF

Springのサーブレット用の設定ファイルです。必須です。

web.xml

xxx\WebContent\WEB-INF

tomcatの設定ファイルです。

Springを使用するために追記する必要があります。

validator.xml

xxx\WebContent\WEB-INF

妥当性チェックの設定ファイルです。SpringModulesがこの機能を提供しています。
validator-rules.xml

xxx\WebContent\WEB-INF

妥当性チェックのルール設定ファイルです。SpringModulesがこの機能を提供しています。
log4j.properties xxx\src

Log4jの設定ファイルです。

messages_ja.properties xxx\src

メッセージリソースの設定ファイルです。


主だったファイルは以上です。



参考:

・Springとは? (機能一覧と概要)

・DIの設定ファイルを書くには?




前回は、SpringのWEBプロジェクトを作成するまでを見てみました。

ここでは実際にWEBを表示するまでの、基本的なSpringの設定を見てみましょう。

プロジェクトとSpringのXMLファイルを作成していない人は、前回の記事 を読んで設定してください。



【簡単なWEBを作ってみる】

さて、実際に表示できるWEBを作ってみましょう。

やることは、JSPファイルを作成することと、Springの設定ファイルを作成することです。



<web.xml>


<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee "
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance "
xsi:schemaLocation="
http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd ">


<display-name>test</display-name>


<!-- spring用のリスナー(これ書かないとapplicationContext.xmlを読まない) -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>


<!-- エンコード(パラメタの文字コードを指定します) -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- springMVC用の設定(MVCにstrutsを利用する場合は不要) -->
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>


<!--

 以下は、パスの拡張子がhtmlのときにSpringを適用するように記述しています。

-->
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>

<session-config>
<session-timeout>60</session-timeout>
</session-config>

<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>


<!--
 読み込むspring設定ファイルの指定
 xxx-servlet.xmlはデフォルトで読み込むのでここで設定する必要がありません。
-->

<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>


</web-app>



<test-servlet.xmlファイル>

次に、SpringのWEBの中心設定をするファイルです。

前回の記事の設定で作成したファイルに以下の記述をします。



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans "
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance "
xmlns:aop="http://www.springframework.org/schema/aop "
xmlns:tx="http://www.springframework.org/schema/tx "
xmlns:util="http://www.springframework.org/schema/util "
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd ">


<!--
 メッセージリソースの設定。
 bean名はmessageSourceにする必要があります。
-->

<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>


<!-- ExceptionResolver (例外発生時にどのページを表示するか?)
bean名はexceptionResolverにする必要があります。

コントローラやビジネスロジックでは,この方法で例外処理を行えます。
ただし、ビューやフィルタで例外が発生した場合は処理されません。
web.xmlでerror-pageタグを利用して,例外処理を行う必要があります。
http://itpro.nikkeibp.co.jp/article/COLUMN/20080523/303910/?P=2
-->
<bean id="exceptionResolver"
class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.Exception">/WEB-INF/jsp/error.jsp</prop>
</props>
</property>
</bean>


<!-- HandlerMapping (パス名とControllerを結び付けます)-->
<bean id="handlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/top.html">fileNameController</prop>
<prop key="/next.html">fileNameController</prop>
</props>
</property>
</bean>



<!--

 サンプルController
 コントローラは、xxx-servlet.xmlファイル内に記述しないと、messageのLocaleが設定されない。
-->

<bean id="fileNameController" class="org.springframework.web.servlet.mvc.UrlFilenameViewController">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />

</bean>

</beans>



<applicationContext.xmlファイル>

このファイルは、今回は特に変更しません。




<jspファイル: WebContent/WEB-INF/index.jsp>

 index.jspというファイル名で作成します。

 Springで管理しているページに遷移するリンクを作るだけの特に意味の無いページです。


<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd ">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>単純なindex画面です</title>
</head>
<body>
index画面<br>
<br>
<a href="top.html">Springの画面へ</a>

</body>
</html>




<Spring用のjspファイル: WebContent/WEB-INF/jsp/top.jsp>

 jspフォルダの下にtop.jspというファイル名で作成します。

 上記のSpringのhandlerMapping が働いているかを見るためのJSPです。


<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd ">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>単純な画面です</title>
</head>
<body>
単純トップ画面<br>
<br>
<a href="next.html">次の画面</a>

</body>
</html>



<Spring用のjspファイル: WebContent/WEB-INF/jsp/next.jsp>

 jspフォルダの下にnext.jspというファイル名で作成します。 


<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd ">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>単純な次の画面です</title>
</head>
<body>
画面遷移を見るためのサンプル<br>
<br>
<a href="top.html">前の画面に戻る</a>

</body>
</html>





  
【終了です!】

プロジェクトを右クリックして、実行⇒サーバーで実行 を選択しましょう。

どうです?うまく動きましたか?



【Spring設定ファイルの説明】

test-servlet.xmlについて、軽く触れます。


ここでは、以下のような設定をしています。


fileNameController
 単純にJSPファイルを表示するだけのコントローラーです。

 表示するJSPファイルの実パスは以下のように推測します。

   prefix + URLパス名(拡張子なし) + suffix

 上記のXMLファイルのfileNameControllerの設定を見てみてください。

 例えば、http://localhost:8080/test/top.html

 というパスに対しては、/WEB-INF/jsp/top.jsp というJSPファイルを検索して表示します。

 パスと拡張子を自由に変更できるので、便利です。


handlerMapping
 次に見るのはURLパスの設定です。

 このクラスは、あるURLにアクセスに来たときに、そのコントローラを起動するかを決めます。

 上記のXMLファイルのhandlerMappingの設定を見てみてください。

 例えば以下の設定の場合、

  <prop key="/top.html">fileNameController</prop>


 /top.html というアクセスに対しては、fileNameControllerを起動することになります。
 このコントローラは、先ほど見たように、JSPファイルのパスを推測して表示します。

 つまり、/WEB-INF/jsp/top.jsp を表示することになります。



messageSourceexceptionResolverは、ここでは使用していませんが、

遅かれ早かれ使用することになると思います。

ですので、紹介がてら記述しておきました。

興味があれば調べてみてください。


なんとなくやっていることがわかりましたでしょうかえっ
割と簡単でしょ?



ここでは、Springで用意されているコントローラを使用したので、自作のコントローラを作成しませんでしたが、

他の記事でそのあたりは見て行こうと思います。



参考:

・Springを使用するには?

・DIの設定ファイルを書くには?

・Springとは? (機能一覧と概要)

・Springにおけるファイル構成
・XMLとは?

ここでは、Springを使用したWEB開発をするための設定を見ていきます。

SpringのWEBプロジェクトを作成するところまでを記述します。

具体的なWEB作成サンプルは次の記事 で書いてみようと思います。


まず、tomcatをインストールしておいてください。
そして、eclipseを起動してください。



【SpringIDEプラグインのインストール】

Springで使用するeclipseのプラグインをインストールします。

設定ファイルの間違いを指摘してくれたり、AOPで織り込みをしている箇所を教えてくれたりする優れものです。


eclipseの、ヘルプメニュー ⇒ ソフトウェア更新 を選択します。

以下のウィンドウが開きます。


  Java Springの逆引きメモ
  「http://springide.org/updatesite/ 」を入力して、OKを押します。


  すると、自動で必要なものをすべてインストールしてくれます!



【プロジェクトの作成】

次に、SpringのWEBプロジェクトを設定していきます。


eclipseの、ファイルメニュー ⇒ 新規 ⇒ その他 を選択します。

以下のウィンドウが開きます。

 
  Java Springの逆引きメモ
  「動的Webプロジェクト」を選択して次へを押します。

  

  

  プロジェクト名に「test」と入力してください。

  そして、下記のターゲットランタイムの新規ボタンを押すと、以下のウィンドウが開きます。

  Java Springの逆引きメモ
                    開いたウィンドウで、インストールしているtomcatのバージョンを選択します。

                    選択したら、次へボタンを押します。


  
  Java Springの逆引きメモ
  参照ボタンを押し、tomcatをインストールしているディレクトリを選択します。

  終了ボタンを押します。

  するとプロジェクトが作成されます。



  次は、プロジェクトにSpringを適用します。

  プロジェクトを右クリックし、

  Spring Tools ⇒ Add Spring Project Nature を選択します。
  Java Springの逆引きメモ
  するとプロジェクトのところにSというマークが付きます。

  これでプロジェクトの設定は完了です。  



【Springに必要なファイルを設定する】

ここからは、実際にSpringの設定ファイルの作り方を見ていきます。

ファイルメニュー ⇒ 新規 ⇒ その他 を選択します。

  
  Java Springの逆引きメモ
  Spring Bean Definitionを選択し、次へを押します。

  すると、以下のウィンドウが表示されます。


  
  Java Springの逆引きメモ
  WEB-INFの配下に「test-servlet.xml」を作成します。

  Springでは、必ず必要なXMLファイルが2つあります。

  「プロジェクト名-servlet.xml」 と 「applicationContext.xml」です。

  ここではプロジェクト名をtestにしたので、test-servlet.xmlをまず作成します


  

  次へボタンを押すと以下のウィンドウが表示されます。

  
  Java Springの逆引きメモ
  Spring設定ファイルで使用できる名前空間が一覧になっています。

  ここでは、AOPとtxとutilくらいを選択しておけばいいと思います。

  やっていくうちに他のものも見ていってください。


  終了ボタンを押すとXMLが作成されます。

  XMLファイルには、Sのマークが付いていると思います。

  これは、eclipseでSpringとしてこのファイルを管理している、ということを示します。



  この後、同じ方法で「applicationContext.xml」ファイルも追加してみてください。



【ライブラリをインストールします】

以下の場所にライブラリをドラッグ&ドロップします。ダウンロードはこちら

 場所: WebContent/WEB-INF/lib

 ライブラリ:

     spring-2.5.6.jar

     spring-web-2.5.6.jar

     spring-webmvc-2.5.6.jar

 ダウンロード場所:

     http://www.springsource.org/download

     ダウンロードはdownloadリンクを押して、次の画面で「Community Download」のリンクを押してください。

     メールなどの情報を入力後、ダウンロード画面に行きます!

      


 ※このサンプルでは上記3つのライブラリで十分です。

  実際には、必要に応じて増やしていきましょう。


  

ここまでは大丈夫でしょうか?

大丈夫でしたら、実際に次の記事 を見て、WEBを表示するところまでのSpringの設定をして見ましょう!



参考:

・DIの設定ファイルを書くには?






前回 、ロールによってリンクの表示を制御する方法を見ました。

これはロールが増えたり変更されることが無ければ特に問題ないのですが、

増えることがあると、関係するすべてのJSPファイルを修正しなければならないので大変です。


ですので、ログインユーザがあるパスに対して認可OKかどうかを判定する機能がほしくなります。


その方法を見てみましょう!


ただし、最初に言っておくとSpringで用意しているtaglibには存在しないようです。

(自分が調べた限りでは)


ですので、機能を自作することになります。

しかし、さすがSpringラブラブ!

再利用しやすい形になっていて、割と簡単に実現できます。


では、早速見てみましょう!



【パスの認可をチェックするクラスのサンプル】

package utils;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.security.AccessDeniedException;
import org.springframework.security.intercept.web.FilterSecurityInterceptor;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
/**
 * 認可を模擬するメソッドを提供する。Spring Testのjarが必要。
 * @author admin
 */

public class MySpringSecurityUtils {
 /** 認可を行うフィルタのbean名。
 SpringSecurityのバージョンが上がったときは名称が変わるかも */
 private static String FILTER_NAME = "_filterSecurityInterceptor";
 
 private ServletContext context;
 private HttpServletRequest request;
 private FilterSecurityInterceptor filterSecurityInterceptor;
 
 /**
  * コンストラクタ
  * @param context [in]
  * @param req     [in]現在のセッションなどを保持したリクエスト
  */
 public MySpringSecurityUtils(ServletContext context, HttpServletRequest req){
  this.context = context;
  this.request = req;
 }
 
 
 /**
  * 指定のパスが閲覧許可されるか?
  * 設定したリクエストにあるセッションユーザ情報で認可チェックをする。
  * @param servletPath [in]パス。(例:"/topMenu.do")
  * @return 許可される場合true
  * @see #isAllowedUrl(String, ServletContext, HttpServletRequest, FilterSecurityInterceptor)
  */
 public boolean isAllowed(String servletPath){
  //認可を行うフィルタを取得する
  if(this.filterSecurityInterceptor == null){
   WebApplicationContext wac = getWebApplicationContext(context);
   this.filterSecurityInterceptor
           = (FilterSecurityInterceptor)wac.getBean(FILTER_NAME);
  }
  
  return isAllowedUrl(servletPath, this.context, 
                    this.request, this.filterSecurityInterceptor);
 }
  
 /**
  * 指定のパスが閲覧許可されるか?
  * @param servletPath
  * @param context
  * @param req
  * @return
  * @see #isAllowedUrl(String, ServletContext, HttpServletRequest, FilterSecurityInterceptor)
  */
 public static boolean isAllowedUrl(String servletPath, 
   ServletContext context, HttpServletRequest req){
  
  //ApplicationContextを取得
  WebApplicationContext wac = getWebApplicationContext(context);
  
  //認可を行うフィルタを取得する
  FilterSecurityInterceptor f
           = (FilterSecurityInterceptor)wac.getBean(FILTER_NAME);
  
  //
  return isAllowedUrl(servletPath, context, req, f);
 }
 
 
 /**
  * 指定のパスの認可をチェックする(実処理)
  * 設定したリクエストにあるセッションユーザ情報で認可チェックをする。
  * @param servletPath
  * @param context
  * @param req
  * @return 認可OKのときtrue。
  * @exception RuntimeException 予期せぬエラー
  */
 protected static boolean isAllowedUrl(String servletPath, 
  ServletContext context, HttpServletRequest req, 
  FilterSecurityInterceptor f){
  
  try{
   //何もしないFiterChainを生成する(無名クラス)
   FilterChain fc = new FilterChain(){
    @Override
    public void doFilter(ServletRequest arg0, ServletResponse arg1)
      throws IOException, ServletException {  
    }
   };
   //ダミーのRequestクラスにダミーのデータを設定する
   MockHttpServletRequest mockReq = new MockHttpServletRequest();
   mockReq.setSession(req.getSession());
   mockReq.setMethod(req.getMethod());
   mockReq.setServletPath(servletPath);
   
   //認可のフィルタ実行
   f.doFilter(mockReq, new MockHttpServletResponse(), fc);
   
  }catch(AccessDeniedException e){
   return false;
  }catch(Exception e){
   throw new RuntimeException("認可模擬チェック中に予期せぬエラー。", e);
  }
  
  return true;
 }
 
 
 static protected WebApplicationContext
                  getWebApplicationContext(ServletContext context){
  WebApplicationContext wac = WebApplicationContextUtils
        .getRequiredWebApplicationContext(context);
  return wac;
 }
 
}




【JSPのサンプル】

<%@ page language="java" pageEncoding="utf-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"% >
<%@ taglib prefix="sec" uri="http://www.springframework.org/security/tags " %>
<%@ page import="utils.MySpringSecurityUtils" %>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%
MySpringSecurityUtils display

= new MySpringSecurityUtils(session.getServletContext(), request);
%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<link rel="stylesheet" href="css/default.css" type="text/css" />

<title>トップページ</title>
</head>
<body>
<a href="logout">ログアウト</a>
<br />


<br />
トップページです!
<br />
:
<br />
<c:if test="<%=display.isAllowed("/test.do")%>">
<a href="test.do">テスト(自作認可チェックによるリンク表示)</a><br />
</c:if>




【説明】

上記でやっていることは、認可をチェックするクラス(MySpringSecurityUtils)を作成し、

JSP上でnewして使用しているだけです。

使い方はそれ程むずかしくないですよね。

 <c:if test="<%=display.isAllowed("/test.do")%>">

c:ifタグで認可が通るかどうかをチェックして、表示・非表示を制御します!




では、ここからは実装を見ていきましょう。


メインは、以下のメソッドです。

isAllowedUrl(String servletPath,
ServletContext context, HttpServletRequest req, FilterSecurityInterceptor f);


<全体的な動作>

 全体としては、以下のことをしています。

 ①Springの設定ファイル(ApplicationContext)から、認可処理をしているフィルタを取り出します。

  (上記プログラム上ではwac.getBean(FILTER_NAME)の部分)

  取り出しているクラスは、SpringSecurityの設定ファイルのタグでいうと、

  sec:intercept-urlタグにあたるフィルタで、FilterSecurityInterceptorクラスにあたります。

  tomcatのフィルタインターフェースを継承しています。


 ②ダミーのリクエストを作成し、本物のリクエストのセッションを設定します。

  (上記プログラム上ではMockHttpServletRequestにあたります)

  セッション内にログイン情報が入っているからです。


 ③②のダミーリクエストを引数にして、FilterSecurityInterceptor.doFilter()を呼び出します。

  (上記プログラム上ではf.doFilter(mockReq, new MockHttpServletResponse(), fc);

  このクラスの実装では、認可に失敗するとAccessDeniedException例外が発生します。



SpringSecurityが用意しているクラスをそのまま使用しているのが分かるでしょうか。

Springは再利用しやすく、なかなかつくりが良いですねー。



<フィルターについて>

SpringSecurityでは、ログイン機能、認証機能、認可機能、ログアウト機能、etc、のように、機能を分解して、その1つ1つをフィルターとして実装しています。

そしてそれらのフィルターをチェーンして、順番に実行していきます。

今、これらの機能のうちで流用したい機能は認可機能です。

そこでそれを取り出せばいいのですが、取り出すにはどうしたらよいのでしょう??


実はこれら1つ1つはSpringのApplicationContextに設定されていて、bean名も固定で決まっているのです。

FilterSecurityInterceptorは、"_filterSecurityInterceptor"というbean名です。

この名称で取り出しています。



【補足】

SpringSecurityには他にもフィルターがあり、拡張することもできます。

また、自作したフィルターを既存のフィルタと入れ替えたり、指定の場所に入れ込んだりできます。

かなり拡張性が高く設計されています!

こうした機能拡張や設定はまた別の記事で見ていこうと思います。



参考:

・ロールによって画面のリンクの表示/非表示を制御するには?