はじめに
SpringBoot3.xにアップグレードするとログインできなくなるプロジェクトがありました。
本家のアップグレードのドキュメントを見ても特に変更漏れが無くハマった為、記載しておきます。
同一の症状が出ている人は参考にしてみてください。
環境
- SpringBoot 3.1.6
- Java 17
- spring-boot-starter-security
- spring-session-jdbc
- UsernamePasswordAuthenticationFilterクラスを使用して拡張
ログイン後DBにセッションは書きこまれていましたが、何故かログインすらできませんでした。
簡易修正方法
SecurityFilterChainにてHttpSecurityで設定できるrequireExplicitSaveにfalseを入れるだけです。
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http, AuthenticationManager authenticationManager) throws Exception {
http
//...
.securityContext(
(securityContextCustomizer) -> {
securityContextCustomizer.requireExplicitSave(false);
})
//...
}
falseはSpringBoot2の頃のデフォルトとのこと。
これでアップグレード以前と同じ状態になりログイン可能になります。
原因
どうやら認証まわりで仕様変更があったらしくデフォルトで使われるクラスが変更されたそうです。
具体的にはSecurityContextPersistenceFilterからSecurityContextHolderFilterに変更になったとのこと。
SecurityContextPersistenceFilterは現在非推奨となっておりソースを覗いても@Deprecatedがつけられています。
正しくはSecurityContextの明示的な保存をする必要性があるらしいです。
試しに明示的に書いてみる
UsernamePasswordAuthenticationFilterを継承したクラスに書いてみました。
attemptAuthenticationメソッドにてコンテキストを書きこんでます。
SecurityContextRepositoryはコンストラクタで作成してます。
ドキュメントに乗っているデフォルトの方法と同じにして親クラスに渡すことにしました。
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
private SecurityContextRepository securityContextRepositoryCustom;
public CustomAuthenticationFilter(){
this.securityContextRepositoryCustom =new DelegatingSecurityContextRepository(
new RequestAttributeSecurityContextRepository(),
new HttpSessionSecurityContextRepository());
super.setSecurityContextRepository(securityContextRepositoryCustom);
}
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {
//...
SecurityContext securityContext = SecurityContextHolder.getContext();
SecurityContextHolder.setContext(securityContext);
securityContextRepositoryCustom.saveContext(securityContext, request, response);
//...
return this.getAuthenticationManager().authenticate(authRequest);
}
//...
}
一応セッションが落ちずに動きました。
備考
明示的な保存方法は断片的なソースのみでサンプルが見つかりませんでした。
HttpSecurityにて明示的に書き込むような方法が思いつかず、リクエスト単位で通るフィルタに記述したところ動きました。
明示的な保存方法についてもし正しい書き方を知っている方がいらっしゃいましたらご教授願います。