spring
spring-security-core
spring-boot

Spring Security(Spring Boot)で、PreAuthentication

More than 3 years have passed since last update.


シングルサインオン基盤との連携

Spring Securityでは、シングルサインオン基盤との連携機能もあるらしい。

ちとややこしいが、ひとまず動いたっぽいので、メモ。

下記に機能追加、修正を加える。

http://qiita.com/masatsugumatsus/items/f80819218bb1fa424470


作成するクラス

1.MyPreAuthenticatedFilter


事前にシングルサインオン基盤等でリクエストに埋め込まれた認証情報(principals,credentials)を取り出す。

  AbstractPreAuthenticatedProcessingFilterを継承して作成する。


2.MyUserDetailsService


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


3.MyUserAuthority、MyAdminAuthority


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



ざっくりとした流れの解説


  • PreAuthentifiatedFilterで認証情報を取り出し、フィルタ内の処理で、AuthenticationManagerに認証処理を委譲し、認証結果となるAuthenticationオブジェクトをセッションとスレッドローカルに保存する。(Spring Securityは基本、Authenticationオブジェクトを見て、認証、アクセス制御を行う。)

  • AuthenticationManagerのデフォルト実装であるProviderManagerは内部のAuthenticationProviderに処理を委譲する。今回は、Spring Securityが提供するPreAuthenticatedAuthenticationProviderをそのまま設定する。

  • PreAuthenticatedAuthenticationProvider内部ではreAuthentifiatedFilterで取得された認証情報からAuthenticationUserDetailsServiceを利用して、ユーザー情報を作成しているので、今回はAuthenticationUserDetailsServiceを拡張したクラスを設定する。


ソース


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();
}

}



MyPreAuthenticatedFilter.java

public class MyPreAuthenticatedFilter extends AbstractPreAuthenticatedProcessingFilter{

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

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

}



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;
}

}



MyAdminAuthority.java

public class MyAdminAuthority implements GrantedAuthority {

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

}



MyUserAuthority.java

public class MyUserAuthority implements GrantedAuthority {

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

}


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