Spring Security
の UsernamePasswordAuthenticationFilter
はユーザー名/パスワードを使って認証を行うフィルタです。
リクエストの本文がJSONの時はこのままではうまくいきません。(HttpServletRequest#getParameter
を使っているため)
なので、このクラスを継承した自前のクラスを作成しました。(このフィルタクラス自体の内容は省略)
この自前のクラスを登録するときに
@Override
protected void configure(HttpSecurity http) throws Exception {
//省略
JsonBodyUsernamePasswordAuthenticationFilter f = new JsonBodyUsernamePasswordAuthenticationFilter();
//いくつか省略
http.addFilterAt(f, UsernamePasswordAuthenticationFilter.class);
}
としていたのですが、これだと一部の機能が UsernamePasswordAuthenticationFilter
と同じようには動きません。
(例 ログイン成功時にセッションIDが変更されないなど)
この問題の対処のメモを書きます。
-
Spring Boot
のバージョンは2.5.5 -
Spring Security
のバージョンは5.5.2
です。
先に結論
フィルターの登録には 上記の方法は使わず FormLoginConfigurer を参考に AbstractAuthenticationFilterConfigurer
を継承したクラスを作成し、このクラス経由でフィルターを登録するようにしました。
コード例
public class JsonBodyLoginConfigurer<H extends HttpSecurityBuilder<H>> extends
AbstractAuthenticationFilterConfigurer<H, JsonBodyLoginConfigurer<H>, JsonBodyUsernamePasswordAuthenticationFilter> {
public JsonBodyLoginConfigurer() {
super(new JsonBodyUsernamePasswordAuthenticationFilter(), null);
}
@Override
public JsonBodyLoginConfigurer<H> loginPage(String loginPage) {
return super.loginPage(loginPage);
}
@Override
protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
return new AntPathRequestMatcher(loginProcessingUrl, "POST");
}
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//省略
http.apply(new JsonBodyLoginConfigurer<>())
.loginPage("/login")
.loginProcessingUrl("/login")
.permitAll()
//省略
}
これで ログイン成功時にセッションIDが変更されるようになりました。
セッションIDの変更はどこでされているのか?
ChangeSessionIdAuthenticationStrategy で行われています。
このメソッドは AbstractAuthenticationProcessingFilter (UsernamePasswordAuthenticationFilter
の親クラス)で認証成功時に呼ばれるようになっています。
UsernamePasswordAuthenticationFilter
で検証したときは CompositeSessionAuthenticationStrategy で集約されており、他には CsrfAuthenticationStrategy が含まれていました。
(この CompositeSessionAuthenticationStrategy
はどこで作られているかは追ってないです)
なぜ セッションIDが変更されなかったのか?
AbstractAuthenticationProcessingFilter
で宣言されている sessionStrategy
フィールド
private SessionAuthenticationStrategy sessionStrategy = new NullAuthenticatedSessionStrategy();
が NullAuthenticatedSessionStrategy
のままになっていました。
このため、認証に成功した場合もセッションIDの変更が行われないままになっていました。
sessionStrategy
はどこでセットされるのか
コードを追いかけたところ AbstractAuthenticationFilterConfigurer#configure メソッドで セットされていました。
このメソッドでは SessionAuthenticationStrategy
以外の クラスもセットしているようです。
addFilterAt
で登録したときは、これらのクラスのセットが行われていなかったので、セッションIDの変更が行われなかったようです。
じゃあどうしようか?🤔
ここで設定しているように http.getSharedObject(SessionAuthenticationStrategy.class)
を WebSecurityConfigurerAdapter
の継承クラスのconfigure
メソッドで使おうとしましたが、このタイミングでは get
してもまだ設定されていないので null
が返却されます。
悩んだ結果 AbstractAuthenticationFilterConfigurer
の継承クラスを使うのが良いと思い、最初に記載した方法になりました。
終わり
この方法が正しいかどうかもわかりませんが、とりあえずメモとして残しておきます。