Spring Securityで認証失敗や権限不足の例外をハンドリングする方法を徹底解説
生徒
「Spring Securityでログインに失敗したときって、どうやってエラーメッセージを表示したり、リダイレクトを制御するんですか?」
先生
「それはAuthenticationFailureHandlerを使って処理をカスタマイズすることでできますよ。また、ログイン済みでもアクセス権限がない場合にはAccessDeniedHandlerを使います。」
生徒
「じゃあ、自分でそのハンドラーを作成して登録すれば、好きなように制御できるんですね!」
先生
「その通りです。これから実際の使い方を順番に見ていきましょう!」
1. Spring Securityの例外ハンドリングとは?
Spring Securityはデフォルトでログイン失敗時やアクセス拒否時に自動的なエラーページへ遷移させますが、これを自分で制御したい場合は、専用のハンドラークラスを作成して設定します。
具体的には以下の2つを使います。
- AuthenticationFailureHandler:認証失敗時(ログイン失敗)を制御
- AccessDeniedHandler:権限不足時(403エラー)を制御
それぞれを独自に実装して、Spring Securityの設定に組み込むことで、認証処理の流れを柔軟にカスタマイズできます。
2. 認証失敗時を制御するAuthenticationFailureHandlerの実装方法
ログインフォームでパスワードを間違えたなど、認証に失敗したときの処理をカスタマイズするにはAuthenticationFailureHandlerを実装します。
以下にその例を示します。
@Component
public class CustomAuthFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception)
throws IOException, ServletException {
response.sendRedirect("/login?error=true");
}
}
このように、ログイン失敗時に指定のURL(ここでは/login?error=true)へリダイレクトするように設定できます。
3. 権限不足時のエラー制御:AccessDeniedHandlerの使い方
ログインは成功しているが、アクセス先に対する権限が足りない場合はAccessDeniedHandlerを使います。
@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request,
HttpServletResponse response,
AccessDeniedException accessDeniedException)
throws IOException, ServletException {
response.sendRedirect("/access-denied");
}
}
このようにして、403エラー画面の代わりに独自のエラーページへ誘導することができます。
4. Spring Securityへのハンドラー登録方法
上記で作成したAuthenticationFailureHandlerとAccessDeniedHandlerを、Spring Securityの設定クラス内で登録します。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private CustomAuthFailureHandler customAuthFailureHandler;
@Autowired
private CustomAccessDeniedHandler customAccessDeniedHandler;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.formLogin(form -> form
.loginPage("/login")
.failureHandler(customAuthFailureHandler)
)
.exceptionHandling(ex -> ex
.accessDeniedHandler(customAccessDeniedHandler)
);
return http.build();
}
}
これで、ログイン失敗時にはCustomAuthFailureHandlerが、権限不足時にはCustomAccessDeniedHandlerが呼ばれるようになります。
5. 認証エラー情報をログイン画面に表示する
エラー時に/login?error=trueのようにクエリパラメータを付けてリダイレクトすることで、ログイン画面にエラーメッセージを表示できます。
<form method="post" action="/login">
<input type="text" name="username" />
<input type="password" name="password" />
<button type="submit">ログイン</button>
<div th:if="${param.error}">
<span class="text-danger">ユーザー名またはパスワードが違います。</span>
</div>
</form>
このようにして、ユーザーにわかりやすくエラーを通知できます。
6. よくある失敗例とその対処法
ハンドラーが呼ばれないというトラブルもあります。次のような原因が考えられます。
- SecurityFilterChainにハンドラーの登録を忘れている
- カスタムハンドラーに
@Componentがついていない - URLマッピングが誤っていて正しいパスに遷移できない
これらはエラーログやデバッグで確認しながら1つずつ潰していくのがポイントです。
7. 認証成功時も制御したい場合は?
AuthenticationSuccessHandlerを使うことで、ログイン成功後の処理も自由に制御できます。
@Component
public class CustomAuthSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication)
throws IOException, ServletException {
response.sendRedirect("/dashboard");
}
}
これでログイン成功後に/dashboardへ遷移するようになります。
8. 403ページをデザインする方法
/access-deniedにリダイレクトされたときに表示するページを自分で作るには、HTMLを用意してそこに説明やデザインを加えます。
<!DOCTYPE html>
<html>
<head>
<title>アクセス拒否</title>
</head>
<body>
<h1>アクセスが拒否されました</h1>
<p>このページにアクセスする権限がありません。</p>
<a href="/">トップページへ戻る</a>
</body>
</html>
まとめ
Spring Securityにおける例外ハンドリングの全体像
本記事では、Spring Securityを利用したWebアプリケーション開発において重要となる、 認証失敗や権限不足といった例外をどのようにハンドリングするかについて詳しく解説してきました。 Spring Securityはデフォルトでも一定の動作を提供してくれますが、 実務レベルではユーザー体験やセキュリティ要件に合わせて、 ログイン失敗時やアクセス拒否時の挙動を細かく制御する必要があります。 その際に中心となるのが、AuthenticationFailureHandlerやAccessDeniedHandlerといった 専用のハンドラーインターフェースです。
AuthenticationFailureHandlerは、ユーザー名やパスワードの誤りなど、 認証そのものが失敗した場合に呼び出されます。 これを使うことで、ログイン画面へのリダイレクト先を変更したり、 クエリパラメータを付与してエラーメッセージを表示したりといった柔軟な制御が可能になります。 一方で、AccessDeniedHandlerはログイン済みであるにもかかわらず、 権限不足によってアクセスが拒否された場合に利用されます。 両者の役割を正しく理解することが、Spring Securityの例外ハンドリングを使いこなす第一歩です。
設定クラスとハンドラー登録の重要性
カスタムハンドラーを作成しただけでは、Spring Securityの動作は変わりません。 SecurityFilterChainの設定クラス内で、formLoginやexceptionHandlingに対して 明示的にハンドラーを登録する必要があります。 この設定を忘れてしまうと、「クラスは作ったのに処理が呼ばれない」 という状況に陥りがちです。 また、@Componentアノテーションの付け忘れや、 URLパスの指定ミスもよくある原因として挙げられます。 例外ハンドリングが正しく動作しない場合は、 設定クラスとハンドラーの紐付けを最初に確認する癖をつけましょう。
まとめとしてのサンプル構成イメージ
最後に、認証失敗と権限不足の両方を意識した、 Spring Security設定のシンプルなイメージを整理しておきます。 実際の開発では、この構成をベースに画面遷移やメッセージ表示を調整していくと、 見通しの良いセキュリティ設計が可能になります。
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private CustomAuthFailureHandler customAuthFailureHandler;
@Autowired
private CustomAccessDeniedHandler customAccessDeniedHandler;
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.formLogin(form -> form
.loginPage("/login")
.failureHandler(customAuthFailureHandler)
)
.exceptionHandling(ex -> ex
.accessDeniedHandler(customAccessDeniedHandler)
);
return http.build();
}
}
このように、認証失敗と権限不足を明確に分けてハンドリングすることで、 ユーザーにとって分かりやすく、管理者にとっても保守しやすい構成になります。 Spring Securityは最初こそ設定項目が多く感じられますが、 仕組みを理解してしまえば、セキュアで柔軟な認証基盤を構築できる強力なフレームワークです。
生徒
「認証失敗と権限不足で、使うハンドラーが違う理由がよく分かりました。 なんとなくエラー処理をしていた頃より、頭の中が整理できた気がします。」
先生
「それは良いですね。Spring Securityでは、 どの段階でエラーが起きているのかを意識することがとても大切です。 認証なのか、認可なのかを切り分けられるようになると、 設定の理解も一気に深まります。」
生徒
「ハンドラーを登録し忘れると動かない、という点も印象に残りました。 設定クラスの確認を最初にするようにします。」
先生
「その意識はとても大事ですよ。 セキュリティ設定は小さなミスが大きな挙動の違いにつながります。 一つ一つの設定の意味を理解しながら進めていきましょう。」
生徒
「これでログインエラーや権限エラーを、 自信を持ってカスタマイズできそうです。」
先生
「その調子です。 今回学んだ内容は、実務でも頻繁に使う重要なポイントなので、 ぜひ自分のプロジェクトで試してみてください。」