2018年、Servlet 2.5形式の web.xml で動くSpring MVCに、Spring Securityを追加したときの記録です。Tomcatで起動を確認するなかで、設定を読み込む場所と、実行時のJARの組み合わせを別々に調べました。
servlet-context.xml と web.xml
最初はMVCの servlet-context.xml にSpring Securityの sec:http と認証マネージャーを置いていました。web.xml では ContextLoaderListener が root-context.xml を読み、DispatcherServlet が servlet-context.xml を子コンテキストとして読んでいました。
同じ web.xml で springSecurityFilterChain という名前の DelegatingFilterProxy を登録していました。この構成でルートコンテキストがある場合、プロキシはそこから同名のフィルターBeanを探します。Servlet側の子コンテキストにだけBeanを置くと、ルート側からは参照できません。確認するのはXMLのファイル名や起動順より、プロキシの参照先と、Beanを定義したコンテキストの組み合わせです。
spring-security-context.xml へ分離
MVC用XMLから認証設定を外し、別の spring-security-context.xml へ移します。root-context.xml から取り込むか、web.xml のルート用 contextConfigLocation に追加する構成です。新しいファイルを作っただけでは読み込まれないため、ルート側の読み込み一覧まで確認します。
Tomcatを再起動すると別のエラーが出た
当時の再起動ログでは、webSecurityAuthentication の生成に失敗していました。原因の行をたどると NoSuchMethodError です。ログから、クラス名とメソッド、呼び出し元を抜粋します。
Caused by: java.lang.NoSuchMethodError: org.springframework.security.core.userdetails.User.withUsername(Ljava/lang/String;)Lorg/springframework/security/core/userdetails/User$UserBuilder;
[…]
at byond.team.support.WebSecurityAuthentication.configureGlobal(WebSecurityAuthentication.java:60)User クラスを読み込めても、呼び出す側が期待した withUsername(String) が実行時のクラスにないとこのエラーになります。フィルターの参照先を直したかどうかとは別に、spring-security-config が使うAPIと、実際にロードされた spring-security-core を照合します。
pom.xml と実行時のJARを確認
当時の pom.xml は spring-security-config と spring-security-web が 4.2.2.RELEASE、spring-security-core が 4.1.3.RELEASE でした。公開配布JARを確認すると、Config 4.2.2はこのメソッドを呼び、Core 4.1.3には定義がありません。Core 4.2.2にはすでにあります。
保守中に同じエラーが出たら、Mavenの mvn dependency:tree -Dincludes=org.springframework.security で採用されたバージョンを確認します。WAR配備では WEB-INF/lib とTomcatの共有ライブラリも確認し、POMに書いた版と実行時に読まれる版を突き合わせます。当時はSpring Security関連のバージョンを揃えた後に起動を確認しました。
この記録に出てくる旧版は、ログを読むための履歴です。現行のServlet構成では、採用中のSpring Securityに対応したJava設定で SecurityFilterChain を確認します。Spring Bootを使う場合は、対応する依存管理のもとでSecurityの関連モジュールを揃えます。Spring SecurityのJava設定、Mavenの依存関係ツリー
