SPA(Vue.js) + REST API (Spring Boot)で認証処理を含んだアプリケーションを作成した際に、
大事だなと感じたところをまとめました。少しでも参考になれば幸いです。
開発前に事前知識として見ておいたほうがいいリンク
- 認証処理のアーキテクチャ
- オリジン間リソース共有 (CORS)
- Preflight request (プリフライトリクエスト)
- Spring Securityのアーキテクチャ
- セッション管理
- CSRF対策
Spring Securityで利用する各インターフェースの概要を押さえる
利用方法
実装クラスを独自にBeanに追加し利用する方法と、その他用意されているクラスも用いる方法がある。
詳細は、spring.pleiades.ioを確認する。
独自実装クラスを作成し利用する例を以下に示す。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.exceptionHandling()
.authenticationEntryPoint(authenticationEntryPoint())
.accessDeniedHandler(accessDeniedHandler());
}
@Bean
AccessDeniedHandler accessDeniedHandler() {
return new CustomAccessDeniedHandler();
}
AuthenticationProvider
主に、認証処理の実装を提供するためのインターフェースである。
AuthenticationEntryPoint
未認証のユーザーが認証の必要なAPIにアクセスしたときの処理を行うためのインタフェースである。
AccessDeniedHandler
ユーザーは認証済みだが未認可のリソースへアクセスしたときの処理を行うためのインタフェースである。
AuthenticationSuccessHandler
認証成功後の処理を行うためのインターフェースである。
AuthenticationFailureHandler
認証失敗後の処理を行うためのインターフェースである。
LogoutSuccessHandler
ログアウト処理成功後の処理を行うためのインターフェースである。
クライアント側での実装
- 毎回認証状態が維持できているかのヘルスチェックを、初期表示時や各処理終了後に行う。
- APIから返ってきたHTTPステータスコードを元に、リダイレクトの処理を行う。
バックエンド側は、クライアント側に関与しないため、HTTPステータスコードを返却するのみとする。
// ログイン処理
const res = await axios.post('xxxxxxx', this.request)
if(res.status === 200){
// リダイレクト処理
}
バックエンド側での実装
- リダイレクトなどの処理はCORSが絡むためクライアント側へ委譲し、HTTPステータスコードの返却のみを行う。
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
// 簡素な例だが、HTTPステータスコードの返却のみを行っている
response.setStatus(HttpStatus.UNAUTHORIZED.value());
}
既知のFilterクラスの1つ前に独自のFilterを追加する
http.addFilterBefore(javax.servlet.Filter filter,
java.lang.Class<? extends javax.servlet.Filter> beforeFilter)
第一引数のfilterには、独自に通したいフィルターを
第二引数のbeforeFilterには、既知のフィルターを記述する。
以下の例では、UsernamePasswordAuthenticationFilterの前に、authenticationFilter()を追加している。
@Bean
CustomAuthenticationFilter authenticationFilter() throws Exception {
CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
filter.setRequiresAuthenticationRequestMatcher(
new AntPathRequestMatcher("/login", "POST"));
filter.setAuthenticationManager(authenticationManagerBean());
filter.setAuthenticationSuccessHandler(authenticationSuccessHandler());
filter.setAuthenticationFailureHandler(authenticationFailuerHandler());
return filter;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 独自のauthenticationFilterをUsernamePasswordAuthenticationFilterの前に通過するように追加
http.addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
CORS対策処理の追加
SPA + REST API構成のアプリケーションでは、CORSが未対策の場合、間違いなく引っかかるかと思うので以下対策が必要です。
CorsConfigurationの詳細は、こちらを参照してください。
private CorsConfigurationSource corsFilter() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true);
configuration.addAllowedOrigin("xxxxx");
configuration.addAllowedMethod(CorsConfiguration.ALL);
configuration.addAllowedHeader(CorsConfiguration.ALL);
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "OPTIONS", "DELETE", "PUT"));
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// CORS対策
http.cors().configurationSource(this.corsFilter());
}