안녕하세요!
이번 포스팅에서는 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); // 위치 독일로 설정
...
}
한국어가 아닌 다른 나라의 위치를 설정하면 다른 나라의 언어로 잘 나오는 것을 확인할 수 있습니다!
(아이디, 비밀번호가 틀렸을 때 나오는 에러 메세지는 이미 한국어로 설정해둬서 한국어로 나오지만, 계정 잠금 같은 다른 메세지들은 설정한 언어로 제대로 표시됩니다)
혹시 글을 읽으면서 잘못된 내용이 있으면 댓글로 알려주시면 감사하겠습니다!
읽어주셔서 감사합니다! 😊
'Backend > SpringBoot' 카테고리의 다른 글
Spring Boot에서 Spring Rest Docs 사용해보기 (4) | 2020.07.24 |
---|---|
Spring Boot Custom Annotation 만들기 (4) | 2020.07.08 |
Spring Security로 로그인/회원가입 프로젝트 (66) | 2020.05.26 |
SpringBoot API 요청 값 검증하고 Validation Exception Handing하기 (1) | 2020.04.14 |
SpringBoot의 POI을 이용해서 엑셀 파일 읽기 (9) | 2020.03.30 |