【Spring MVC + Spring Security】認証エラー時のメッセージをカスタマイズする方法

概要

認証エラー時に表示されるエラーメッセージをカスタマイズする方法についてまとめた。

 

前提

以下の記事の続きとなる。

あわせて読みたい

概要 自作ログイン画面を利用した、簡易的なログイン認証機能の利用方法についてまとめた。   前提 以下の記事の続きとなる。

【Spring MVC + Spring Security】自作ログイン画面を利用して認証する方法

 

メッセージ仕様

エラーメッセージは以下の方針でカスタマイズする。

 

・基本的にはSpring Securityがデフォルトで提供するメッセージプロパティを利用する
・エラーに応じて自作したメッセージで上書きする。

 

補足として、Spring Securityが提供するデフォルトのメッセージを一部紹介する。
※spring-security-core-5.8.5.jarの場合

 

/org/springframework/security/messages_ja.properties


AbstractUserDetailsAuthenticationProvider.badCredentials=ユーザ名かパスワードが正しくありません
AbstractUserDetailsAuthenticationProvider.credentialsExpired=ユーザ認証情報の有効期限が切れています
AbstractUserDetailsAuthenticationProvider.disabled=無効なユーザです
AbstractUserDetailsAuthenticationProvider.expired=ユーザアカウントの有効期限が切れています
AbstractUserDetailsAuthenticationProvider.locked=ユーザアカウントがロックされています
AbstractUserDetailsAuthenticationProvider.onlySupports=UsernamePasswordAuthenticationTokenのみサポートされています

 

 

実装

エラーメッセージを自作プロパティに定義し、Spring Securityフレームワークに適用させる。

 

メッセージ定義

前回の記事にて自作したログイン画面では、画面項目を「ログインID」とした。
そのため、画面項目に合わせたエラーメッセージを定義する。

 

/src/main/resources/messages_ja.properties


######################################
# Spring Security提供のメッセージをカスタマイズ
######################################
AbstractUserDetailsAuthenticationProvider.badCredentials=ログインIDまたはパスワードが間違っています

 

 

メッセージソース定義

自作プロパティからメッセージ内容を取得するため、メッセージソースを使用する。

DIコンテナ(ルートアプリケーションコンテキスト)にBeanを登録して、自作プロパティとSpring Securityが提供しているメッセージプロパティを参照させる。

あわせて読みたい

概要 Springのメッセージ管理を行うMessageSourceを使用して外部ファイルからメッセージを取得する方法についてまとめた。 MessageSourceはプロパティファイルに定義したメッセージを取得する機能を提供する。 […]

【Spring MVC】MessageSourceの基本的な使い方

 

applicationContext.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
         http://www.springframework.org/schema/beans
         http://www.springframework.org/schema/beans/spring-beans.xsd
         		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-3.1.xsd">

	<!-- コンポーネントスキャン -->
	<context:component-scan base-package="com.example.prototype.biz" />

	<!-- ResourceBundleMessageSourceを使用する -->
	<bean id="messageSource"
		class="org.springframework.context.support.ResourceBundleMessageSource">
		<property name="basenames">
			<list>
				<value>messages</value> <!-- 自作 -->
				<value>org/springframework/security/messages</value> <!-- Spring SecurityのJAR内 -->
			</list>
		</property>
	</bean>
	
</beans>

 

<value>messages</value> <!– 自作 –>
<value>org/springframework/security/messages</value> <!– Spring SecurityのJAR内 –>

メッセージソースは、basenamesに指定されたプロパティファイルを上から順に参照する。

そのため、上記のように自作プロパティを先に参照させることで、
両者に同じプロパティキーが存在する場合でも、自作のメッセージが優先して参照される。

これにより、Spring Securityが内部で使用するメッセージを任意の文言で上書きすることができる。

 

メッセージソースの差し替え

DaoAuthenticationProviderにてデフォルトのメッセージソースを利用しているため、これを今回定義したメッセージソースに差し替える。

 

【Spring MVC + Spring Security】認証エラー時のメッセージをカスタマイズする方法_メッセージソース定義
▲Spring Securityにデフォルトで適用されている既存メッセージソースを書き換える

 

SecurityConfig.java


package com.example.prototype.biz.security.config;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public DaoAuthenticationProvider authenticationProvider(MessageSource messageSource) {
        var provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(userDetailsService());
        // メッセージソースの設定
        provider.setMessageSource(messageSource);
        return provider;
    }
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                // CSRF機能を無効
                .csrf(csrf -> csrf.disable())
                .authorizeHttpRequests(auth -> auth
                        // 認証不要のURL
                        .requestMatchers(
                                new AntPathRequestMatcher("/authentication")
                                ).permitAll()
                        
                        // その他のURLは認証が必要
                        .anyRequest().authenticated())

                .formLogin(form -> form
                        // ログイン画面URL
                        .loginPage("/authentication")
                        // UsernamePasswordAuthenticationFilterが認証処理を行うURL
                        .loginProcessingUrl("/authentication/process")
                        // フォームの name="loginId" を認識
                        .usernameParameter("loginId")
                        // フォームの name="pass" を認識
                        .passwordParameter("pass")
                        .defaultSuccessUrl("/", true));

        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails disabledUser = User.withUsername("user")
            .password("{noop}password")
            .roles("USER")
            .disabled(true) // アカウント無効状態
            .build();
        return new InMemoryUserDetailsManager(disabledUser);
    }

}

 

@Bean
public DaoAuthenticationProvider authenticationProvider(MessageSource messageSource) {

DaoAuthenticationProviderをカスタマイズするため、Bean定義する。

 

provider.setMessageSource(messageSource);

メッセージソースを差し替えることで、Spring Securityフレームワークへのメッセージ適用が完了する。(デフォルトのメッセージプロパティと自作プロパティを参照するようになる)

 

エラーメッセージ表示領域定義

ログイン画面にエラーメッセージ表示領域を追加する。

 

login.jsp


<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>ログイン画面</title>
</head>
<body>
	<h2>ログイン</h2>

	<c:if test="${not empty SPRING_SECURITY_LAST_EXCEPTION}">
		<p style="color: red;">
			<c:out value="${SPRING_SECURITY_LAST_EXCEPTION.message}" />
		</p>
	</c:if>
	
	<form action="${pageContext.request.contextPath}/authentication/process" method="post">
		<label for="loginId">ログインID:</label>
		<input type="text" id="loginId" name="loginId" autofocus />
		<br />
		<label for="password">パスワード:</label>
		<input type="password" id="pass" name="pass" />
		<br />
		<input type="submit" value="ログイン" />
	</form>
	<br>
</body>
</html>

 

<c:if test=”${not empty SPRING_SECURITY_LAST_EXCEPTION}”>

「SPRING_SECURITY_LAST_EXCEPTION」は、Spring Securityが認証失敗時にセッションスコープへエラー内容を保持するためのキーとなる。
ここからエラーメッセージを取得することができる。

 

動作確認

自作したエラーメッセージと、Spring Securityが提供するエラーメッセージが有効になったことを確認する。

 

【Spring MVC + Spring Security】認証エラー時のメッセージをカスタマイズする方法_自作エラー
▲自作プロパティに定義したメッセージ内容

 

【Spring MVC + Spring Security】認証エラー時のメッセージをカスタマイズする方法_デフォルトエラー
▲既存プロパティに定義されたメッセージ内容

 

 

まとめ

 

☑ メッセージソースを利用することで、Spring Securityが内部で使用するメッセージを自作の内容で上書きできる

☑ 定義したメッセージソースを有効にするには、DaoAuthenticationProviderが保持するメッセージソースを差し替える必要がある

☑ 認証失敗時には、SPRING_SECURITY_LAST_EXCEPTIONをキーにエラー情報を取得できる

 

スポンサーリンク