Spring Security(Spring Boot)で、PreAuthentication

#シングルサインオン基盤との連携
Spring Securityでは、シングルサインオン基盤との連携機能もあるらしい。
ちとややこしいが、ひとまず動いたっぽいので、メモ。

下記に機能追加、修正を加える。
http://qiita.com/masatsugumatsus/items/f80819218bb1fa424470

##作成するクラス
 1.MyPreAuthenticatedFilter
> 事前にシングルサインオン基盤等でリクエストに埋め込まれた認証情報（principals,credentials）を取り出す。
　　AbstractPreAuthenticatedProcessingFilterを継承して作成する。

 2.MyUserDetailsService
> 1.で取り出された認証情報から、アプリケーションで利用するユーザー情報を作成する。AuthenticationUserDetailsServiceを拡張する。後述するPreAuthenticatedAuthenticationProviderにセットするが、その場合は、　　　AuthenticationUserDetailsService#loadUserDetailsメソッドの引数は　　　PreAuthenticatedAuthenticationTokenとなるため、AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken>として継承を行う。


 3.MyUserAuthority、MyAdminAuthority
> 一般利用者権限と、管理者権限クラス。GrantedAuthorityを実装する。ま、権限のクラス設計は色々あると思うが、ひとまず、別クラスにした。共通の親クラスを作るべきとは思う。

##ざっくりとした流れの解説
* PreAuthentifiatedFilterで認証情報を取り出し、フィルタ内の処理で、AuthenticationManagerに認証処理を委譲し、認証結果となるAuthenticationオブジェクトをセッションとスレッドローカルに保存する。（Spring Securityは基本、Authenticationオブジェクトを見て、認証、アクセス制御を行う。）
* AuthenticationManagerのデフォルト実装であるProviderManagerは内部のAuthenticationProviderに処理を委譲する。今回は、Spring Securityが提供するPreAuthenticatedAuthenticationProviderをそのまま設定する。
* PreAuthenticatedAuthenticationProvider内部ではreAuthentifiatedFilterで取得された認証情報からAuthenticationUserDetailsServiceを利用して、ユーザー情報を作成しているので、今回はAuthenticationUserDetailsServiceを拡張したクラスを設定する。

###ソース
```java:WebSecurityConfig.java
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

	@Bean
	public AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> 
				authenticationUserDetailsService() {
		return new MyUserDetailsService();
	}

	@Bean
	public PreAuthenticatedAuthenticationProvider 
					preAuthenticatedAuthenticationProvider() {
		PreAuthenticatedAuthenticationProvider provider
			= new PreAuthenticatedAuthenticationProvider();

		provider.
			setPreAuthenticatedUserDetailsService(authenticationUserDetailsService());

		provider.
			setUserDetailsChecker(new AccountStatusUserDetailsChecker());

		return provider;
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth)
			throws Exception {
		auth.authenticationProvider(
				preAuthenticatedAuthenticationProvider()
				);
	}

	@Bean
	public AbstractPreAuthenticatedProcessingFilter preAuthenticatedProcessingFilter()
			throws Exception {
		MyPreAuthentifiatedFilter filter = new MyPreAuthenticatedFilter();

		filter.setAuthenticationManager(authenticationManager());
		return filter;
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.addFilter(preAuthenticatedProcessingFilter());

		http.authorizeRequests().antMatchers("/", "/home").permitAll()
				// 管理者ページの追加
				.antMatchers("/admin").hasRole("ADMIN").anyRequest()
				.authenticated();
		http.formLogin().loginPage("/login").permitAll().and().logout()
				.permitAll();
	}

}
```

```java:MyPreAuthenticatedFilter.java
public class MyPreAuthenticatedFilter extends AbstractPreAuthenticatedProcessingFilter{

	@Override
	protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
		//リクエストからユーザーID等のユーザー情報を抽出
		return "user01";
	}

	@Override
	protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
		//リクエストから証明書情報を抽出。DB等にある場合もある？
		return "";
	}

}
```

```java:MyUserDetailsService.java
public class MyUserDetailsService implements AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> {

	@Override
	public UserDetails loadUserDetails(PreAuthenticatedAuthenticationToken token)
			throws UsernameNotFoundException {
		String userName=(String)token.getPrincipal();
		Object credentials = token.getCredentials();
		
		//principalとcredentialsを利用して、ユーザー情報を作成。今回は適当にサンプル作成
		Collection<GrantedAuthority> authorities =new HashSet<GrantedAuthority>() ;
		authorities.add(new MyAdminAuthority());
		User user = new User(userName,"",authorities);
		
		//パスワード渡すとかあり得ないと思うので、別途自分で作成するべき。
		return user;
	}

}
```

```java:MyAdminAuthority.java
public class MyAdminAuthority implements GrantedAuthority {
	
	@Override
	public String getAuthority() {
		return "ROLE_ADMIN";	
	}

}
```

```java:MyUserAuthority.java
public class MyUserAuthority implements GrantedAuthority {

	@Override
	public String getAuthority() {
		return "ROLE_USER";
	}

}
```

ドキュメントを見ても、狙い通りの動きをするかは正確には分からないので、ソース見るしかない。でも、ソースが十分分かり易いので、それほど困らない。ドキュメントはコンセプト、概要、詳細はソース読めっていうのは、プログラマ向けの成果物としては合理的。Springのドキュメントは豊富で素晴らしいというのは、当然前提としての感想です。