0
0

【SpringSecurity】5.4.6から5.4.7に上げた時に発生したFilter順序不正について

Posted at

背景

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.addFilterAfterHttpSecurity.addFilterBeforeが正常に設定されなかったため、前後が変わってしまうというのが発生しておりました(少なくとも挙動を見る限りは)。
具体的には上記のaddFilterAfteraddFilterBeforeと同じ挙動をしております。
そして通る順番としては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実装自体は良いのですが、前後で影響が出るような実装は保守性を下げるだけなので、影響しないような実装を心がけるべきです。
今後新規開発を行う際は、可能な限り分かりやすい実装を目指したいと改めて思いました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0