[스프링 부트+스프링 시큐리티+타임리프] 로그인 실패 핸들러

jyleever·2022년 6월 24일
2

로그인에 실패 했을 경우, 어떤 이유로 실패했는지 사용자가 정확하게 이해할 수 있게 직접 오류 메세지를 커스터마이징할 수 있다. Spring Security 에서 관련 인터페이스를 제공한다.

1. SecurityConfig에 예외 핸들러 설정

  • AuthenticationFailureHandler
  • failureHandler

로그인 실패와 관련된 예외들은 WebSecurityConfigurerAdapter 상속 클래스(SecurityConfig)의 configure(HttpSecurity http) 메서드에 failureHandler()를 추가해야 예외를 캐치할 수 있다.

  • 이 failureHandler() 메서드는 파라미터로 AuthenticationFailureHandler 인터페이스 구현체를 받는다.
@RequiredArgsConstructor
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	...
	/* 로그인 실패 핸들러 의존성 주입 */
	private final AuthenticationFailureHandler customFailureHandler;
	
		
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
            ...
			.failureHandler(customFailureHandler) /* 로그인 실패 핸들러 */
            ...
	}
}

2. 예외 핸들러 생성 - CustomAuthFailureHandler

LoginFailHandler

로그인 실패를 핸들링하는 커스텀 핸들러, AuthenticationFailureHandler 인터페이스를 구현함.

  • AuthenticationFailureHandler 인터페이스를 implements해야 한다.
    이 때 AuthenticationFailureHandler 인터페이스를 구현한 구현체 SimpleUrlAuthenticationFailureHandler 를 extends 한다.

  • 굳이 SimpleUrlAuthenticationFailureHandler 를 사용한 이유
    해당 클래스가 제공해주는 setDefaultFailureUrl() 메서드를 활용하기 위함. 해당 메서드를 활용하면 로그인 실패 시 url을 지정해준다.
    해당 url은 컨트롤러에 매핑되어 있어야 한다.

  • onAuthenticationFailure 메소드를 오버라이딩하여 예외처리, 에러 메시지 띄워준다.
    이 때 에러 메시지의 한글 인코딩이 깨지는 것을 방지하기 위해
    errorMessage = URLEncoder.encode(errorMessage, "UTF-8"); 처리

package com.jy.config.auth;

import java.io.IOException;
import java.net.URLEncoder;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;

public class CustomAuthFailureHandler extends SimpleUrlAuthenticationFailureHandler{

	/*
	 * HttpServletRequest : request 정보
	 * HttpServletResponse : Response에 대해 설정할 수 있는 변수
	 * AuthenticationException : 로그인 실패 시 예외에 대한 정보
	 */
	
	@Override
	public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
			AuthenticationException exception) throws IOException, ServletException {
		
		String errorMessage;
		
		if(exception instanceof BadCredentialsException) {
			errorMessage = "아이디 또는 비밀번호가 맞지 않습니다. 다시 확인해주세요.";
		} else if (exception instanceof InternalAuthenticationServiceException) {
			errorMessage = "내부 시스템 문제로 로그인 요청을 처리할 수 없습니다. 관리자에게 문의하세요. ";
		} else if (exception instanceof UsernameNotFoundException) {
			errorMessage = "존재하지 않는 계정입니다. 회원가입 후 로그인해주세요.";
		} else if (exception instanceof AuthenticationCredentialsNotFoundException) {
			errorMessage = "인증 요청이 거부되었습니다. 관리자에게 문의하세요.";
		} else {
			errorMessage = "알 수 없는 오류로 로그인 요청을 처리할 수 없습니다. 관리자에게 문의하세요.";
		}
		
		errorMessage = URLEncoder.encode(errorMessage, "UTF-8"); /* 한글 인코딩 깨진 문제 방지 */
		setDefaultFailureUrl("/auth/login?error=true&exception="+errorMessage);
		super.onAuthenticationFailure(request, response, exception);
	}
}

SimpleUrlAuthenticationFailureHandler의 super.onAuthenticationFailure

https://www.inflearn.com/questions/289866 참고..

CustomAuthenticationFailureHandler는 super 클래스를 호출하지 않아도 상관없다.
다만 실패 이후 이동할 페이지를 지정할 경우 상위클래스에게 위임하는 것이 더 좋기 때문에 super.onAuthenticationFailure 처리
실패와 관련된 여러가지 후속 처리를 해주기 때문.... 이라고 하는데 완벽히 이해가 가진 않는다

3. UserController

  • CustomAuthFailureHandler 에서 setDefaultFailureUrl()이 호출되었을 때 지정되있는 url을 매핑해줄 컨트롤러
  • 해당 코드에는 RequestParam 으로 넘겨진 error와 exeption을 모델에 담은 후 뷰 리졸브
 	...
	/* 로그인 */
	@GetMapping("/auth/login")
	public String login(@RequestParam(value = "error", required = false) String error,
						@RequestParam(value = "exception", required = false) String exception,
						Model model) {
		
		/* 에러와 예외를 모델에 담아 view resolve */
		model.addAttribute("error", error);
		model.addAttribute("exception", exception);
		return "/user/user-login";
	}

4. 타임리프

<div th:if="error">
   <p id="valid" style="color: red; font-size:12px;"><a th:text="${exception}"></a></p>
</div>

error가 true라면 파라미터 exception의 값을 사용자에게 보여준다.
즉, CustomAuthFailureHandler에서 커스텀한 errormessage를 사용자에게 보여주어 오류 메시지를 정확히 전달한다.


참고
https://velog.io/@dasd412/2.%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0-%EB%A1%9C%EA%B7%B8%EC%9D%B8-%EC%8B%A4%ED%8C%A8-%ED%95%B8%EB%93%A4%EB%A7%81%ED%95%98%EA%B8%B0

0개의 댓글