背景
SpringFrameworkのバージョンを上げたことでエラー画面からログイン画面に戻れなくなったため、確認したのがきっかけです。
Controllerを通過せずにエラーになっていたため、SpringSecurityのTRACEログを確認したところ、SessionManagementFilter通過後の挙動が異なっていることがわかりました。
【前提】SpringSecurityとは?
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements
https://spring.io/projects/spring-security
つまり、SpringSecurityは認証、アクセス制限等をよしなにいい感じにセキュアにやってくれるフレームワークです。
【前提】Filterとは?
SpringSecurityは認証、アクセス制限をSecurity FilterというServlet Filterで確認しています。
例えば、CSRF対策やセッション使用可否判定、トークン確認等を行っています。
5.4.6と5.4.7の比較
今回発生した現象は同名のFilterを定義した場合の挙動です。
※既に非推奨になっている実装ですので、絶対に流用しないでください。
@Configuration
@EnableWebSecurity
public class SpringSecurityCOnfig extends WebSecurityConfigurerAdapter {
private Filter expiredSessionFilter() {
SessionManagementFilter smf = new SessionManagementFilter(new HttpSessionSecurityContextRepository());
smf.setInvalidSessionStrategy(null);
return smf;
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequests().authenticated()
.and()
.formLogin();
http.addFilterAfter(expiredSessionFilter(), SessionManagementFilter.class);
}
}
上記の実装の場合、デフォルトのSessionManagementFilter
と追加で実装したexpiredSessionFilter
(SessionManagementFilter
という名前です)が同名になります。
同名のFilterがどうなるかというと、5.4.6まではHttpSecurity.addFilterAfter
とHttpSecurity.addFilterBefore
が正常に設定されなかったため、前後が変わってしまうというのが発生しておりました(少なくとも挙動を見る限りは)。
具体的には上記のaddFilterAfter
はaddFilterBefore
と同じ挙動をしております。
そして通る順番としてはexpiredSessionFilter
->SessionManagementFilter
の順番になります。
5.4.7ではその不具合が正常化されたため、正常に順番が設定されるようになりました。
逆に言えば、この不具合に依存していた処理は動かなくなります。
今回の実装で言うとSessionManagementFilter
->expiredSessionFilter
と正常な順番になるものの、expiredSessionFilter
が最初になる挙動が仕様上正しい場合は、改修する必要があります。
ではどのように改修すれば良いのか?
addFilterBefore
にすれば万事解決です。
@Configuration
@EnableWebSecurity
public class SpringSecurityCOnfig extends WebSecurityConfigurerAdapter {
private Filter expiredSessionFilter() {
SessionManagementFilter smf = new SessionManagementFilter(new HttpSessionSecurityContextRepository());
smf.setInvalidSessionStrategy(null);
return smf;
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login").permitAll()
.anyRequests().authenticated()
.and()
.formLogin();
http.addFilterBefore(expiredSessionFilter(), SessionManagementFilter.class);
}
}
これにより通る順番がexpiredSessionFilter
->SessionManagementFilter
になるため、元に戻ります。
対応コミット
以下の対応で正常な順番に設定されるようになりました。
ここまで読んで...
ここまで読んで理解された方はわかると思いますが、結論前後で影響が出るような実装そのものが良くないです。
Filter実装自体は良いのですが、前後で影響が出るような実装は保守性を下げるだけなので、影響しないような実装を心がけるべきです。
今後新規開発を行う際は、可能な限り分かりやすい実装を目指したいと改めて思いました。