본문 바로가기

Backend/SpringBoot

Spring Security Error Message 커스텀하기

안녕하세요!

이번 포스팅에서는 Spring Security에서 로그인에 실패했을 때 나오는 에러 메세지를 커스텀 해보도록 하겠습니다! 🤗

이번 포스팅에서 사용할 스프링 시큐리티가 적용된 프로젝트는 전 게시글에서 진행한 프로젝트입니다.

 

전체 코드는 Github에서 확인이 가능합니다.


💻 구현

1. Config 파일 작성하기

가장 먼저, Message 인코딩 설정을 위해 Config 파일을 작성해보겠습니다.

SecurityMessageConfig.java

/**
 * 메세지 관련 Config 파일
 */
@Configuration
public class SecurityMessageConfig {

  @Bean
  public MessageSource messageSource() {

    Locale.setDefault(Locale.KOREA); // 위치 한국으로 설정
    ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();

    messageSource.setDefaultEncoding("UTF-8"); // 인코딩 설정
    messageSource.setBasenames("classpath:message/security_message", "classpath:org/springframework/security/messages"); // 커스텀한 properties 파일, security properties 파일 순서대로 설정
    return messageSource;
  }

}

MessageSource를 반환해주는 messageSource() 메소드를 만들어줍니다.

만약 한국어가 아닌 다른 언어를 지정하고 싶으면 Locale.KOREA를 다른 위치로 변경하면 됩니다.

그 후에 ReloadableResourceBundleMessageSource를 설정해주면 되는데,

인코딩을 UTF-8로 설정해주고, message 관련 properties 파일이 있는 위치를 지정해주어야합니다.

저는 메세지를 제가 커스텀 한 설정 파일에서 찾고, 없으면 기본 설정 파일에서 가져오게 하겠습니다.

setBasenames()으로 커스텀 할 설정 파일의 위치를 먼저 지정해주고(아직 만들진 않았습니다), 에러 메세지가 기본적으로 정의되어있는 기본 설정파일의 위치인 classpath:org/springframework/security/messages를 지정해주었습니다.

 

2. properties 파일 작성하기

이제 특정 에러가 났을 때 커스텀 할 메세지를 작성해보도록 하겠습니다.

resources에 message라는 폴더를 만들어주고,

security_message.properties라는 이름으로 설정 파일을 생성해줍니다.

 

security_message.properties

AbstractUserDetailsAuthenticationProvider.badCredentials = 아이디나 비밀번호를 확인해주세요.

기존 아이디랑 비밀번호가 틀렸을 때 나오는 오류 메세지는 '자격 증명에 실패했습니다'인데,

이 메세지를 변경하기 위해 properties 파일에 새롭게 설정해줍니다.

 

3. 로그인 페이지 수정하기

login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
  <head>
    <meta charset="UTF-8">
    <title>login</title>
  </head>
  <body>
			
		...

    <div th:if="${session['SPRING_SECURITY_LAST_EXCEPTION']} != null">
      <span th:text="${session['SPRING_SECURITY_LAST_EXCEPTION'].message}"></span>
    </div>

  </body>
</html>

에러 메세지를 가져오는 태그를 달아주고,

메세지는 SPRING_SECURITY_LAST_EXCEPTION 세션의 message를 가져오게 합니다.


📝 테스트

아무것도 저장되지 않는 채로 로그인을 누르면,

커스텀 한 에러 메세지인 '아이디나 비밀번호를 확인해주세요'가 나오는 것을 확인할 수 있습니다.

 

그리고 회원가입을 한 다음에 로그인을 하면 무조건 잠금 처리되게 설정했기 때문에 아래 메세지가 표시됩니다.

따로 지정하지 않는 에러 메세지는 기본으로 제공해주는 메세지가 나오는 것을 확인할 수 있습니다.

기본 메세지는 아래와 같습니다.

수정하고 싶은 메세지가 있으면 커스텀 메세지 설정 파일에서 수정하면 됩니다.

messages_ko_KR.properties

더보기
AbstractAccessDecisionManager.accessDenied = 접근이 거부되었습니다.
AbstractLdapAuthenticationProvider.emptyPassword = 비밀번호가 맞지 않습니다.
AbstractSecurityInterceptor.authenticationNotFound = SecurityContext에서 Authentication 객체를 찾을 수 없습니다.
AbstractUserDetailsAuthenticationProvider.badCredentials = 자격 증명에 실패하였습니다.
AbstractUserDetailsAuthenticationProvider.credentialsExpired = 자격 증명 유효 기간이 만료되었습니다.
AbstractUserDetailsAuthenticationProvider.disabled = 유효하지 않은 사용자입니다.
AbstractUserDetailsAuthenticationProvider.expired = 사용자 계정의 유효 기간이 만료 되었습니다.
AbstractUserDetailsAuthenticationProvider.locked = 사용자 계정이 잠겨 있습니다.
AbstractUserDetailsAuthenticationProvider.onlySupports = UsernamePasswordAuthenticationToken만 지원합니다.
AccountStatusUserDetailsChecker.credentialsExpired = 자격 증명 유효 기간이 만료되었습니다.
AccountStatusUserDetailsChecker.disabled = 유효하지 않은 사용자입니다.
AccountStatusUserDetailsChecker.expired = 사용자 계정의 유효 기간이 만료 되었습니다.
AccountStatusUserDetailsChecker.locked = 사용자 계정이 잠겨 있습니다.
AclEntryAfterInvocationProvider.noPermission = domain object {1}에 대한 권한이 Authentication {0}에 없습니다.
AnonymousAuthenticationProvider.incorrectKey = 제공된 AnonymousAuthenticationToken에는 필요로하는 key가 없습니다.
BindAuthenticator.badCredentials = 자격 증명에 실패하였습니다.
BindAuthenticator.emptyPassword = 비밀번호 항목이 비어 있습니다.
CasAuthenticationProvider.incorrectKey = 제공된 CasAuthenticationToken에는 필요로 하는 key가 없습니다.
CasAuthenticationProvider.noServiceTicket = 검증을 위한 CAS 서비스 티켓을 제공할 수 없습니다.
ConcurrentSessionControlAuthenticationStrategy.exceededAllowed = 최대 세션 허용 수 {0}개를 초과하였습니다.
DigestAuthenticationFilter.incorrectRealm = 응답 realm 이름 {0}과 시스템 realm 이름 {1}이 일치하지 않습니다.
DigestAuthenticationFilter.incorrectResponse = 응답이 정확하지 않습니다.
DigestAuthenticationFilter.missingAuth = 'auth' QOP(quality of protection)를 위한 digest 값은 필수 항목입니다. 현재 header 값은 {0}입니다.
DigestAuthenticationFilter.missingMandatory = digest 값은 필수 항목입니다. 현재 header 값은 {0}입니다.
DigestAuthenticationFilter.nonceCompromised = Nonce 토큰이 손상되었습니다. 현재 nonce 값은 {0}입니다.
DigestAuthenticationFilter.nonceEncoding = Nonce 값이 Base64로 인코딩 되어있지 않습니다. 현재 nonce 값은 {0}입니다.
DigestAuthenticationFilter.nonceExpired = Nonce의 유효 기간이 만료되었거나 시간이 초과되었습니다.
DigestAuthenticationFilter.nonceNotNumeric = Nonce 토큰의 첫 글자는 숫자로 시작해야 합니다. 현재 nonce 값은 {0}입니다.
DigestAuthenticationFilter.nonceNotTwoTokens = Nonce는 두 개의 토큰을 만들어야 합니다. 현재 nonce 값은 {0}입니다.
DigestAuthenticationFilter.usernameNotFound = {0} ID를 찾을 수 없습니다.
JdbcDaoImpl.noAuthority = {0} 사용자는 권한이 없습니다.
JdbcDaoImpl.notFound = {0} 사용자를 찾을 수 없습니다.
LdapAuthenticationProvider.badCredentials = 자격 증명에 실패하였습니다.
LdapAuthenticationProvider.credentialsExpired = 자격 증명 유효 기간이 만료되었습니다.
LdapAuthenticationProvider.disabled = 유효하지 않은 사용자입니다.
LdapAuthenticationProvider.expired = 사용자 계정의 유효 기간이 만료 되었습니다.
LdapAuthenticationProvider.locked = 사용자 계정이 잠겨 있습니다.
LdapAuthenticationProvider.emptyUsername = ID에 공백은 허용되지 않습니다.
LdapAuthenticationProvider.onlySupports = UsernamePasswordAuthenticationToken만 지원합니다.
PasswordComparisonAuthenticator.badCredentials = 자격 증명에 실패하였습니다.
PersistentTokenBasedRememberMeServices.cookieStolen = 로그인 상태 유지를 위한 토큰이 일치하지 않습니다. 이전에 사용한 토큰이 타인으로부터 탈취 당했을 수 있습니다.
ProviderManager.providerNotFound = {0}을 위한 AuthenticationProvider를 찾을 수 없습니다.
RememberMeAuthenticationProvider.incorrectKey = 제공된 RememberMeAuthenticationToken에는 필요로 하는 key가 없습니다.
RunAsImplAuthenticationProvider.incorrectKey = 제공된 RunAsUserToken에는 필요로 하는 key가 없습니다.
SubjectDnX509PrincipalExtractor.noMatching = subjectDN%: {0} 내에 매칭되는 패턴이 없습니다.
SwitchUserFilter.noCurrentUser = 요청한 사용자를 찾을 수 없습니다.
SwitchUserFilter.noOriginalAuthentication = Authentication 객체의 원본을 찾을 수 없습니다.

 

이번에는 Locale을 Korea가 아닌 다른 나라로 지정해서 다국어 지원이 가능한지 확인해보겠습니다.

 

SecurityMessageConfig.java

@Configuration
public class SecurityMessageConfig {

  @Bean
  public MessageSource messageSource() {
    Locale.setDefault(Locale.GERMANY); // 위치 독일로 설정
		...

}

 

한국어가 아닌 다른 나라의 위치를 설정하면 다른 나라의 언어로 잘 나오는 것을 확인할 수 있습니다!

(아이디, 비밀번호가 틀렸을 때 나오는 에러 메세지는 이미 한국어로 설정해둬서 한국어로 나오지만, 계정 잠금 같은 다른 메세지들은 설정한 언어로 제대로 표시됩니다)

 

혹시 글을 읽으면서 잘못된 내용이 있으면 댓글로 알려주시면 감사하겠습니다!

읽어주셔서 감사합니다! 😊