どうやってSpring Security Configurerが建てたインスタンスを変更する

# *org.springframework.security.config.annotation.ObjectPostProcessor*インタフェース

Spring Security configurer/builderは、一つ、あるいはひとつ以上の*ObjectPostProcessor*インタフェースのインスタンスを受け入れることができることが設計される。下は*ObjectPostProcessor*の定義

```java
public interface ObjectPostProcessor<T> {
  <O extends T> O postProcess(O object);
}
```

**postProcess()**メソッドの役目は渡した引数を変更すること。

*ObjectPostProcessor*を実装したクラスは一つがある：*org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor*。このクラスの役目は

1. 渡したインスタンスを初期化する(*ApplicationContext*で依存性の注入)
2. *ApplicationContext*が*SmartInitializingSingleton*と*DisposableBean*インタフェースのメソッドを呼ぶ時、前処理したインスタンスの同じメソッドを呼ぶ

*AutowireBeanFactoryObjectPostProcessor*シングルトンBeanが作られ、そしてConfigurer/Builderが作られるときこのシングルトンBeanを注入する。(もしConfigurer/Builderがひとつ以上の*ObjectPostProcessor*インタフェースのインスタンスを受け入れることができる場合はこのシングルトンBeanは処理列の一番目)

一部のConfigurer/Builderがひとつ以上の*ObjectPostProcessor*インタフェースのインスタンスを受け入れることができる。だからこれが建てたインスタンスを変更することができる。

# 例

まず[サンプルアプリケーション](https://github.com/saladlam/spring-noticeboard)のソースコードを取り、以下*info.saladlam.example.spring.noticeboard.support*パッケージのクラスを作る。

```java
package info.saladlam.example.spring.noticeboard.support;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.config.annotation.ObjectPostProcessor;

public class CustomizeObjectPostProcessor implements ObjectPostProcessor<Object> {

	private static final Logger log = LoggerFactory.getLogger(CustomizeObjectPostProcessor.class);
	private String fromClass;

	public CustomizeObjectPostProcessor(String fromClass) {
		this.fromClass = fromClass;
	}

	public <T> T postProcess(T object) {
		log.info("Instance pass into {}: {}", this.fromClass, object.getClass().getName());
		return object;
	}

}
```

そして下のように*info.saladlam.example.spring.noticeboard.config.WebSecurityConfig*クラスの**configure()**メソッドを変える。

```java
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
				// must put more restricted rule at first
				.antMatchers("/manage/*/approve").hasAuthority("ADMIN")
				.antMatchers("/manage/**").hasAuthority("USER")
			.and()
				.httpBasic().disable()
				.formLogin()
					.loginPage("/login")
					.loginProcessingUrl("/loginHandler")
					.failureUrl("/login?error=true")
					.permitAll()
			.and()
				.logout().logoutSuccessUrl("/");

		http.authorizeRequests().withObjectPostProcessor(new CustomizeObjectPostProcessor("authorizeRequests"));
		http.formLogin().addObjectPostProcessor(new CustomizeObjectPostProcessor("formLogin"));
		http.httpBasic().addObjectPostProcessor(new CustomizeObjectPostProcessor("httpBasic"));
		http.logout().addObjectPostProcessor(new CustomizeObjectPostProcessor("logout"));
	}
```

最後に以下のログ出力が見える。

```
2019-09-16 12:25:54.503  INFO 5512 --- [  restartedMain] i.s.e.s.n.s.CustomizeObjectPostProcessor : Instance pass into formLogin: org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint
2019-09-16 12:25:56.121  INFO 5512 --- [  restartedMain] i.s.e.s.n.s.CustomizeObjectPostProcessor : Instance pass into httpBasic: org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint
2019-09-16 12:25:57.009  INFO 5512 --- [  restartedMain] i.s.e.s.n.s.CustomizeObjectPostProcessor : Instance pass into httpBasic: org.springframework.security.web.authentication.logout.HttpStatusReturningLogoutSuccessHandler
2019-09-16 12:25:58.145  INFO 5512 --- [  restartedMain] i.s.e.s.n.s.CustomizeObjectPostProcessor : Instance pass into logout: org.springframework.security.web.authentication.logout.LogoutFilter
2019-09-16 12:25:59.732  INFO 5512 --- [  restartedMain] i.s.e.s.n.s.CustomizeObjectPostProcessor : Instance pass into authorizeRequests: org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler
2019-09-16 12:26:00.798  INFO 5512 --- [  restartedMain] i.s.e.s.n.s.CustomizeObjectPostProcessor : Instance pass into authorizeRequests: org.springframework.security.access.vote.AffirmativeBased
2019-09-16 12:29:01.005  INFO 5512 --- [  restartedMain] i.s.e.s.n.s.CustomizeObjectPostProcessor : Instance pass into authorizeRequests: org.springframework.security.web.access.intercept.FilterSecurityInterceptor
2019-09-16 12:29:03.527  INFO 5512 --- [  restartedMain] i.s.e.s.n.s.CustomizeObjectPostProcessor : Instance pass into formLogin: org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
2019-09-16 12:29:03.933  INFO 5512 --- [  restartedMain] i.s.e.s.n.s.CustomizeObjectPostProcessor : Instance pass into httpBasic: org.springframework.security.web.authentication.www.BasicAuthenticationFilter
```

*CustomizeObjectPostProcessor*クラスの設計で、クラスに渡したインスタンスが変更できる

# 付録：Spring Security configurer/builderに建てたインスタンスクラス表

| Spring Security configurer/builder                                                                                      |  建てたインスタンスクラス                                                                                                                                                                                                                                          |
| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer | org.springframework.security.authentication.dao.DaoAuthenticationProvider                                                                                                                                                                                     |
| org.springframework.security.config.annotation.web.configurers.CsrfConfigurer                                           | org.springframework.security.web.csrf.CsrfFilter                                                                                                                                                                                                              |
| org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer                              | org.springframework.security.web.access.ExceptionTranslationFilter                                                                                                                                                                                            |
| org.springframework.security.config.annotation.web.configurers.HeadersConfigurer                                        | org.springframework.security.web.header.HeaderWriterFilter                                                                                                                                                                                                    |
| org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer                              | org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy, org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy, org.springframework.security.web.session.SessionManagementFilter |
| org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer                                | org.springframework.security.web.context.SecurityContextPersistenceFilter                                                                                                                                                                                     |
| org.springframework.security.config.annotation.web.configurers.RequestCacheConfigurer                                   | org.springframework.security.web.savedrequest.RequestCacheAwareFilter                                                                                                                                                                                         |
| org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer                                      | org.springframework.security.authentication.AnonymousAuthenticationProvider                                                                                                                                                                                   |
| org.springframework.security.config.annotation.web.configurers.ServletApiConfigurer                                     | org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter                                                                                                                                                                           |
| org.springframework.security.config.annotation.web.configurers.LogoutConfigurer                                         | org.springframework.security.web.authentication.logout.LogoutFilter                                                                                                                                                                                           |
| org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer                     | org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler, org.springframework.security.access.vote.AffirmativeBasedorg.springframework.security.web.access.intercept.FilterSecurityInterceptor                                   |
| org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer                                      | org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter                                                                                         |