[Spring Security] 인증 실패 오류 다루기
스프링 시큐리티에서는 인증 관련 예외가 발생한 경우 AbstractUserDetailsAuthenticationProvider 가 클래스 내부의 hideUserNotFountExceptions 변수를 확인하고 발생한 예외를 BadCredentialsException 예외로 변환해 어떤 예외가 종류의 예외가 발생했는지 감춘다.
// AbstractUserDetailsAuthenticationProvider.class
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
() -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
"Only UsernamePasswordAuthenticationToken is supported"));
String username = determineUsername(authentication);
boolean cacheWasUsed = true;
UserDetails user = this.userCache.getUserFromCache(username);
if (user == null) {
cacheWasUsed = false;
try {
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
}
catch (UsernameNotFoundException ex) {
this.logger.debug("Failed to find user '" + username + "'");
if (!this.hideUserNotFoundExceptions) {
throw ex;
}
throw new BadCredentialsException(this.messages
.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
}
Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
}
try {
this.preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
}
catch (AuthenticationException ex) {
if (!cacheWasUsed) {
throw ex;
}
// There was a problem, so try again after checking
// we're using latest data (i.e. not from the cache)
cacheWasUsed = false;
user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
this.preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
}
this.postAuthenticationChecks.check(user);
if (!cacheWasUsed) {
this.userCache.putUserInCache(user);
}
Object principalToReturn = user;
if (this.forcePrincipalAsString) {
principalToReturn = user.getUsername();
}
return createSuccessAuthentication(principalToReturn, authentication, user);
}
인증 방식에 따라 다른 예외를 뱉도록 설정하려면 AuthenticationProvider를 새로 정의한 후 시큐리티 설정에서 추가해 줘야 한다.
public class CustomDaoAuthenticationProvider extends DaoAuthenticationProvider {
public CustomDaoAuthenticationProvider(UserDetailsService userDetailsService) {
super();
setHideUserNotFoundExceptions(false);
setUserDetailsService(userDetailsService);
}
}
// SecurityConfig.java
@Bean
SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
...
.authenticationProvider(customDaoAuthenticationProvider());
return http.build();
}
기본적인 동작은 스프링 시큐리티가 사용하는 AbstractuserDetailsAuthenticationProvider 대로 사용하되, 프레임워크가 제공하는 기능 중 몇 가지만 수정해 애플리케이션의 요구사항을 반영하자.
@RequiredArgsConstructor
public class CustomAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
private final UserDAO userDAO;
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
String username = request.getParameter("username");
String errorMessage = "아이디와 비밀번호를 확인해주세요. <br> 비밀번호 5회 오류 시 30분간 계정이 잠깁니다.";
if (exception.getCause() instanceof AccountLockException) {
errorMessage = exception.getCause().getMessage();
request.getSession().setAttribute("error", errorMessage);
super.onAuthenticationFailure(request, response, exception);
return;
}
if (username != null) {
UserVO userVO = userDAO.getUserInfoByAcctId(username);
if (userVO != null) {
userVO.setRetryCnt(userVO.getRetryCnt() + 1);
if(userVO.getRetryCnt() >= 5) userVO.setLockTime(LocalDateTime.now().plusMinutes(30));
userDAO.updateUserInfo(userVO);
}
}
request.getSession().setAttribute("error", errorMessage);
super.onAuthenticationFailure(request, response, exception);
}
}
public class AccountLockException extends AuthenticationException {
public AccountLockException(String msg) {
super(msg);
}
public AccountLockException(String msg, Throwable cause) {
super(msg, cause);
}
}
인증 시 발생하는 오류에 따라 클라이언트에게 보여줄 예외 메세지를 다르게 설정할 때 사용한다.
스프링 시큐리티 프레임워크는 인증과 인가 프로세스를 기본적으로 제공하니..
제공하는 프로세스 중 특정 부분에서 수정이 필요한 경우 애플리케이션을 디버그 모드로 실행시키고 어떤 부분에서 어떻게 설정하는지 파악한 후 해당 부분을 조작하자.
보통 상속과 오버라이드를 사용한다.
반응형
'Solutions' 카테고리의 다른 글
[PDF.js] PDF.js 완벽 가이드 (3) | 2024.11.07 |
---|---|
[Spring Batch] 메타데이터 테이블과 시퀀스 (0) | 2024.11.05 |
[SQL Server] 지원하지 않는 TLS 버전 설정 (1) | 2024.06.05 |
대용량 파일 업로드 처리 (30GB) (1) | 2023.12.03 |
[JavaScript] Shadow DOM 다루기 (0) | 2023.11.22 |
댓글
이 글 공유하기
다른 글
-
[PDF.js] PDF.js 완벽 가이드
[PDF.js] PDF.js 완벽 가이드
2024.11.07 -
[Spring Batch] 메타데이터 테이블과 시퀀스
[Spring Batch] 메타데이터 테이블과 시퀀스
2024.11.05 -
[SQL Server] 지원하지 않는 TLS 버전 설정
[SQL Server] 지원하지 않는 TLS 버전 설정
2024.06.05 -
대용량 파일 업로드 처리 (30GB)
대용량 파일 업로드 처리 (30GB)
2023.12.03