概要
Spring Security 5.7.xではWebSecurityConfigurerAdapter
を拡張する記法が非推奨となり、SecurityFilterChain
をBeanとして登録する記法が推奨されています。
そこで自作したAuthenticationFilter
とAuthenticationProvider
を使う設定ファイルを書き直そうと試みました。
@EnableWebSecurity @Configuration public class OldConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(AuthenticationProviderBuilder auth) { auth.authenticationProvider(new MyAuthenticationProvider()); } @Override protected void configure(HttpSecuirty http) { MyAuthenticationFilter filter = new MyAuthenticationFilter(); filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/my-login", "POST")); filter.setAuthenticationManager(super.authenticationManagerBean()) http .formLogin(formLogin -> formLogin .loginPage("/my-login-form") .loginProcessingUrl("/my-login") ) .addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class); } }
このときに悩んだこととを二つ紹介します。
1. 自作したAuthenticationProvider
をAuthenticationManager
に登録する方法が分からない
今までWebSecurityConfigurerAdapter#configure(AuthenticationProviderBuilder)
メソッドで自作したMyAuthenticationProvider
をAuthentcationManager
に登録していました。
WebSecurityConfigurerAdapter
クラスを継承しないことに伴い、書き直す必要があります。
UsernamePasswordAuthenticationFilter
を使う場合は今まで通りHttpSecuirty
の設定をすれば解決します。
@Bean public SecurityFilterChain securityFilterChain(HttpSecuirty http) { MyAuthenticationFilter filter = new MyAuthenticationFilter(); filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/my-login", "POST")); filter.setAuthenticationManager(/* どこからかAuthenticationManagerを取得したい */) return http .formLogin(formLogin -> formLogin .loginPage("/my-login-form") .loginProcessingUrl("/my-login") ) .authenticationProvider(new MyAuthenticationProvider()) .addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class); .build(); }
この設定をすると、HttpSecurity#build()
を呼び出したときに、AuthenticationManager
インスタンスが生成されます。
それではAuthenticationManager
を自作したAuthenticationFilter
に設定できなくなってしまいます。
そこでSpring Securityの仕組みに頼らず、自分でAuthenticationManager
を生成しました。
AuthenticationManager
の実装には、ProviderManager
を使用します。
@Bean public SecurityFilterChain securityFilterChain(HttpSecuirty http) { AuthenticationManager authenticationManager = new ProviderManager(new MyAuthenticationProvider()); MyAuthenticationFilter filter = new MyAuthenticationFilter(); filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/my-login", "POST")); filter.setAuthenticationManager(authenticationManager); return http .formLogin(formLogin -> formLogin .loginPage("/my-login-form") .loginProcessingUrl("/my-login") ) .addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class); .build(); }
2. formLoginメソッドで設定した内容と自作したAuthenticationFilterにする設定はどちらが優先されるのか
修正前の設定では、filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/my-login", "POST"))
と、formLogin.loginProcessingUrl("/my-login")
の2か所で認証処理を呼び出すパスを設定しています。
どちらかの設定が無駄になっていると思ったので、FormLoginConfigurer
のドキュメントを確認しました。
Adds form based authentication. All attributes have reasonable defaults making all parameters are optional. If no loginPage(String) is specified, a default login page will be generated by the framework. Security Filters
The following Filters are populated
UsernamePasswordAuthenticationFilter
Shared Objects Created
The following shared objects are populated
AuthenticationEntryPoint
FormLoginConfigurer
はUsernamePasswordAuthenticationFilter
を構築するための設定で、副次的にAuthenticationEntryPoint
オブジェクトを生成しているとあります。
FormLoginConfigurer
でパスを設定しても自作したAuthenticationFilterには設定されませんので、formLoginを使わないことにしました。
@Bean public SecurityFilterChain securityFilterChain(HttpSecuirty http) { AuthenticationManager authenticationManager = new ProviderManager(new MyAuthenticationProvider()); MyAuthenticationFilter filter = new MyAuthenticationFilter(); filter.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/my-login", "POST")); filter.setAuthenticationManager(authenticationManager) return http .exceptionHandling(exceptionHandling -> exceptionHandling .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/my-login-form")) ) .addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class); .build(); }
最後に
フレームワークの設定はかなり小手先の領域にはなりますが、それでも自分が何をやりたくて、フレームワークのどの部分を拡張してくと良いのかを考えるのはとても面白かったです。 そのためにも、使用するフレームワークやライブラリのリファレンスに載っているような内容は理解しておきたいです。